subql icon indicating copy to clipboard operation
subql copied to clipboard

Using solana-dex-parser with mappings

Open mogupta opened this issue 3 months ago • 7 comments

added the following to mapping

import { DexParser, SolanaTransaction } from 'solana-dex-parser';
const parser = new DexParser();

Test

import { subqlTest } from "@subql/testing";
import { Transfer } from "../types/models";

// Example test for Transfer entity
subqlTest(
  "handleTransfer test",
  362963837, // block height to process
  [],
  [
    // Expected output entities
    {
      id: "test-signature-123-0",
      from: "source-account-address",
      to: "destination-account-address",
      amount: BigInt(100),
      blockNumber: BigInt(362963837),
      transactionHash: "test-signature-123",
      date: new Date("2025-08-28T09:32:21.000Z")
    }
  ],
  "handleTransfer" // handler name
);

Error on test run

2025-08-29T01:13:29.164Z <SolanaDecoder> INFO Loaded IDL for TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA 
2025-08-29T01:13:29.172Z <UnfinalizedBlocks> INFO Unfinalized blocks is disabled 
2025-08-29T01:13:46.914Z <SolanaBlock> WARN Unable to parse log message: Transfer: insufficient lamports 1026178, need 1844400 
2025-08-29T01:13:47.302Z <test-runner> WARN Test: handleTransfer test field due to runtime error 
2025-08-29T01:13:47.302Z <test-runner> WARN Test handleTransfer test failed to run ReferenceError: TextEncoder is not defined
2025-08-29T01:13:47.349Z <StoreCache> WARN Error: SequelizeDatabaseError: relation "test-solana-token-program-starter._metadata" does not exist, Name: SequelizeDatabaseError, Parent: error: relation "test-solana-token-program-starter._metadata" does not exist, Original: error: relation "test-solana-token-program-starter._metadata" does not exist 
2025-08-29T01:13:47.349Z <StoreCache> ERROR Database transaction failed, rolling back DatabaseError: relation "test-solana-token-program-starter._metadata" does not exist
2025-08-29T01:13:47.352Z <Testing> ERROR undefined Error: Testing failed
Cause: DatabaseError: relation "test-solana-token-program-starter._metadata" does not exist
Waiting for the debugger to disconnect...
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
Waiting for the debugger to disconnect...

mogupta avatar Aug 29 '25 01:08 mogupta

Enabling TextEncoder

      sandbox: { atob , TextEncoder },

Error

2025-08-29T01:15:20.883Z <UnfinalizedBlocks> INFO Unfinalized blocks is disabled 
2025-08-29T01:15:22.505Z <SolanaBlock> WARN Unable to parse log message: Transfer: insufficient lamports 1026178, need 1844400 
2025-08-29T01:15:22.771Z <test-runner> WARN Test: handleTransfer test field due to runtime error 
2025-08-29T01:15:22.772Z <test-runner> WARN Test handleTransfer test failed to run Error: Unable to resolve module 'http'. To resolve this you can either:
        Narrow your import. e.g Instead of "import { BigNumber } from 'ethers'" you can use "import {BigNumber} from '@ethersproject/bignumber';"
        Enable the --unsafe flag.
Cause: VMError: Cannot find module 'http'
2025-08-29T01:15:22.800Z <StoreCache> WARN Error: SequelizeDatabaseError: relation "test-solana-token-program-starter._metadata" does not exist, Name: SequelizeDatabaseError, Parent: error: relation "test-solana-token-program-starter._metadata" does not exist, Original: error: relation "test-solana-token-program-starter._metadata" does not exist 
2025-08-29T01:15:22.800Z <StoreCache> ERROR Database transaction failed, rolling back DatabaseError: relation "test-solana-token-program-starter._metadata" does not exist
2025-08-29T01:15:22.804Z <Testing> ERROR undefined Error: Testing failed
Cause: DatabaseError: relation "test-solana-token-program-starter._metadata" does not exist
Waiting for the debugger to disconnect...
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
Waiting for the debugger to disconnect...

mogupta avatar Aug 29 '25 01:08 mogupta

Adding

// Using Node.js built-in TextEncoder/TextDecoder
const { TextEncoder, TextDecoder } = require('util');

// @ts-ignore - We know these exist in the global scope in Node.js
global.TextEncoder = TextEncoder;
// @ts-ignore
global.TextDecoder = TextDecoder as typeof global.TextDecoder;

to index.ts fixed it , also I had to use --unsafe

mogupta avatar Aug 29 '25 01:08 mogupta

Another issue popped up. while using solana-dex-parser, it throws error because it expects a plain object but vm2 wrapper object is being sent. please advise

import { SolanaTransaction } from '@subql/types-solana';
import { Transaction } from '../types';
import { DexParser, SolanaTransaction as dexSolanaTransaction} from 'solana-dex-parser';
export async function handleTransaction(tx: SolanaTransaction): Promise<void> {
  try {
    // Skip if no signatures in the transaction
    if (!tx.transaction?.signatures?.length) return;

    const parser = new DexParser();
    // const plainTx = tx;
    // logger.info();
    const result = parser.parseTrades(tx as unknown as dexSolanaTransaction);
    logger.info(safeStringify(result));
    const signature = tx.transaction.signatures[0];
    const message = tx.transaction.message;
    
    // Get the first and second accounts as from/to addresses
    // Note: This is a simplified example - you might need to adjust based on your specific requirements
    const from = message.accountKeys[0]?.toString();
    const to = message.accountKeys[1]?.toString();
    
    // For token transfers, you would typically need to parse the instruction data
    // This is a simplified example - you'll need to implement the actual parsing based on your needs
    const value = BigInt(0); // You'll need to parse this from the instruction data
    
    // Create and save the transaction record with all required fields
    const txRecord = new Transaction(
      signature,                    // id
      from || '',                   // owner
      to || '',                     // spender
      value,                        // value
      '',                           // contractAddress
      BigInt(tx.block?.blockHeight?.toString() || '0'), // blockNumber
      new Date()                    // date
    );

    await txRecord.save();
    
  } catch (error) {
    logger.error(`Error processing transaction: ${error}`);
    // You might want to handle specific errors differently
    throw error;
  }
}
TypeError [ERR_INVALID_ARG_TYPE]: The "otherBuffer" argument must be an instance of Buffer or Uint8Array. Received an instance of Object
[[Handler]] =
VM2 Wrapper
[[IsRevoked]] =
false
[[Target]] =
TypeError

mogupta avatar Aug 29 '25 04:08 mogupta

this.eventParsers = {
            CREATE: {
                discriminator: constants_1.DISCRIMINATORS.PUMPSWAP.CREATE_POOL_EVENT,
                decode: this.decodeCreateEvent.bind(this),
            },
        readonly CREATE_POOL_EVENT: Uint8Array<ArrayBuffer>;

subql is basically converting discriminator of type Uint8Array into object with index as key e.g. {"0":228,"1":69,"2":165,"3":46,"4":81,"5":203,"6":154,"7":29,"8":177,"9":49,"10":12,"11":210,"12":160,"13":118,"14":167,"15":116};

mogupta avatar Aug 29 '25 06:08 mogupta

@stwiname please advise

mogupta avatar Aug 30 '25 03:08 mogupta

@mogupta, the SubQuery project handlers are running in a sandbox which has some differences from a normal nodejs environment.

You can find more differences here. https://subquery.network/doc/indexer/build/mapping/sandbox.html

The issue you're facing now is that the data passed to the handers is proxy objects so instanceof checks fail. To fix this you could either re-parse the relevant data in the transaction to not be proxied objects. Or you could modify solana-dex-parser

stwiname avatar Aug 31 '25 20:08 stwiname

Hey, @mogupta, we'd like to have a more direct line of communication. If you're interested can you reach out to me on telegram. https://t.me/skottnz

stwiname avatar Sep 03 '25 21:09 stwiname