是否有更好的方法来解码来自不同 DEX 的交换?

问题描述 投票:0回答:1

我正在尝试构建一个 Telegram 机器人,用户可以在其中跟踪多个链上特定钱包上发生的 DEX 交换。

我注意到不同的 DEX 处理掉期的方式不同,并且根据我收集的信息,您必须解码每个收据的日志(以获取所有转账、存款、掉期等),并且根据 DEX,使用特定方法获取准确的进出代币和金额。

Swap() 似乎显示 amount1 和 amount2,但这些值(特别是最后进入钱包的金额)有时不正确(我认为是由于费用)。进入钱包的值似乎是错误的,所以这就是为什么必须使用转账(查看转账顺序、钱包地址以查看它是否在起始地址或终止地址等)。

我觉得必须有一种更简化的方法来处理这个问题(简单地获取准确的输入和输出以及从收据中获取令牌)。

据我所知,没有任何 API 可以简单地聚合和解码所有这些。 Moralis 提供了一个钱包历史记录端点,但它获取钱包的所有交易,而不是只有 1 个交易,而且价格昂贵。

如果可能的话,我希望通过 RPC 完成所有工作,除非 API 使用起来非常便宜。

我现在使用 Base 和 NodeJS。

收据(TXID为:0x72823453899fe671cdf0742f5dd3725d3f52e6f65660d31318e327969a850bcb)看起来像这样(在我的receipts.js文件中):

export const receiptThree = {
  "receipt":{
     "blockHash":"0xbe2cadb0b88ae723bb2d87293f41c64f3242c12fe7e841f3ecc0698d6e3e9701",
     "blockNumber":"0x160cc3e",
     "contractAddress":null,
     "cumulativeGasUsed":"0x762315",
     "effectiveGasPrice":"0x2198a60",
     "from":"0xf4123c2a38d9aa57801a6469b415f8116208feab",
     "gasUsed":"0x3fd0b",
     "l1BaseFeeScalar":"0x8dd",
     "l1BlobBaseFee":"0x1f3e2b33d",
     "l1BlobBaseFeeScalar":"0x101c12",
     "l1Fee":"0x31abeaa5c88",
     "l1GasPrice":"0x1bf934a78",
     "l1GasUsed":"0x175f",
     "logs":[
        {
           "address":"0x000000000022d473030f116ddee9f6b43ac78ba3",
           "blockHash":"0xbe2cadb0b88ae723bb2d87293f41c64f3242c12fe7e841f3ecc0698d6e3e9701",
           "blockNumber":"0x160cc3e",
           "data":"0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000006773824b0000000000000000000000000000000000000000000000000000000000000000",
           "logIndex":"0xc7",
           "removed":false,
           "topics":[
              "0xc6a377bfc4eb120024a8ac08eef205be16b817020812c73223e81d1bdb9708ec",
              "0x000000000000000000000000f4123c2a38d9aa57801a6469b415f8116208feab",
              "0x000000000000000000000000fde4c96c8593536e31f229ea8f37b2ada2699bb2",
              "0x0000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fad"
           ],
           "transactionHash":"0x72823453899fe671cdf0742f5dd3725d3f52e6f65660d31318e327969a850bcb",
           "transactionIndex":"0x24"
        },
        {
           "address":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
           "blockHash":"0xbe2cadb0b88ae723bb2d87293f41c64f3242c12fe7e841f3ecc0698d6e3e9701",
           "blockNumber":"0x160cc3e",
           "data":"0x000000000000000000000000000000000000000000000000000000000003d340",
           "logIndex":"0xc8",
           "removed":false,
           "topics":[
              "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
              "0x000000000000000000000000d56da2b74ba826f19015e6b7dd9dae1903e85da1",
              "0x0000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fad"
           ],
           "transactionHash":"0x72823453899fe671cdf0742f5dd3725d3f52e6f65660d31318e327969a850bcb",
           "transactionIndex":"0x24"
        },
        {
           "address":"0xfde4c96c8593536e31f229ea8f37b2ada2699bb2",
           "blockHash":"0xbe2cadb0b88ae723bb2d87293f41c64f3242c12fe7e841f3ecc0698d6e3e9701",
           "blockNumber":"0x160cc3e",
           "data":"0x000000000000000000000000000000000000000000000000000000000003d4b0",
           "logIndex":"0xc9",
           "removed":false,
           "topics":[
              "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
              "0x000000000000000000000000f4123c2a38d9aa57801a6469b415f8116208feab",
              "0x000000000000000000000000d56da2b74ba826f19015e6b7dd9dae1903e85da1"
           ],
           "transactionHash":"0x72823453899fe671cdf0742f5dd3725d3f52e6f65660d31318e327969a850bcb",
           "transactionIndex":"0x24"
        },
        {
           "address":"0xd56da2b74ba826f19015e6b7dd9dae1903e85da1",
           "blockHash":"0xbe2cadb0b88ae723bb2d87293f41c64f3242c12fe7e841f3ecc0698d6e3e9701",
           "blockNumber":"0x160cc3e",
           "data":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc2cc0000000000000000000000000000000000000000000000000000000000003d4b00000000000000000000000000000000000000001002c925e1b662b91a39fa1b3000000000000000000000000000000000000000000000000000022b0e3a0afc4000000000000000000000000000000000000000000000000000000000000000d",
           "logIndex":"0xca",
           "removed":false,
           "topics":[
              "0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67",
              "0x0000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fad",
              "0x0000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fad"
           ],
           "transactionHash":"0x72823453899fe671cdf0742f5dd3725d3f52e6f65660d31318e327969a850bcb",
           "transactionIndex":"0x24"
        },
        {
           "address":"0x4ed4e862860bed51a9570b96d89af5e1b0efefed",
           "blockHash":"0xbe2cadb0b88ae723bb2d87293f41c64f3242c12fe7e841f3ecc0698d6e3e9701",
           "blockNumber":"0x160cc3e",
           "data":"0x000000000000000000000000000000000000000000000000be44b2865cd2dc95",
           "logIndex":"0xcb",
           "removed":false,
           "topics":[
              "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
              "0x00000000000000000000000029715d8d279cab143a12ff515b40a2b35d7bad37",
              "0x0000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fad"
           ],
           "transactionHash":"0x72823453899fe671cdf0742f5dd3725d3f52e6f65660d31318e327969a850bcb",
           "transactionIndex":"0x24"
        },
        {
           "address":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
           "blockHash":"0xbe2cadb0b88ae723bb2d87293f41c64f3242c12fe7e841f3ecc0698d6e3e9701",
           "blockNumber":"0x160cc3e",
           "data":"0x000000000000000000000000000000000000000000000000000000000003d340",
           "logIndex":"0xcc",
           "removed":false,
           "topics":[
              "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
              "0x0000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fad",
              "0x00000000000000000000000029715d8d279cab143a12ff515b40a2b35d7bad37"
           ],
           "transactionHash":"0x72823453899fe671cdf0742f5dd3725d3f52e6f65660d31318e327969a850bcb",
           "transactionIndex":"0x24"
        },
        {
           "address":"0x29715d8d279cab143a12ff515b40a2b35d7bad37",
           "blockHash":"0xbe2cadb0b88ae723bb2d87293f41c64f3242c12fe7e841f3ecc0698d6e3e9701",
           "blockNumber":"0x160cc3e",
           "data":"0xffffffffffffffffffffffffffffffffffffffffffffffff41bb4d79a32d236b000000000000000000000000000000000000000000000000000000000003d340000000000000000000000000000000000000000000000241dc032d37480a0d2600000000000000000000000000000000000000000000000004879f8717efe6dcfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb2be4",
           "logIndex":"0xcd",
           "removed":false,
           "topics":[
              "0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67",
              "0x0000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fad",
              "0x0000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fad"
           ],
           "transactionHash":"0x72823453899fe671cdf0742f5dd3725d3f52e6f65660d31318e327969a850bcb",
           "transactionIndex":"0x24"
        },
        {
           "address":"0x4ed4e862860bed51a9570b96d89af5e1b0efefed",
           "blockHash":"0xbe2cadb0b88ae723bb2d87293f41c64f3242c12fe7e841f3ecc0698d6e3e9701",
           "blockNumber":"0x160cc3e",
           "data":"0x0000000000000000000000000000000000000000000000000079c590f9d501d4",
           "logIndex":"0xce",
           "removed":false,
           "topics":[
              "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
              "0x0000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fad",
              "0x0000000000000000000000005d64d14d2cf4fe5fe4e65b1c7e3d11e18d493091"
           ],
           "transactionHash":"0x72823453899fe671cdf0742f5dd3725d3f52e6f65660d31318e327969a850bcb",
           "transactionIndex":"0x24"
        },
        {
           "address":"0x4ed4e862860bed51a9570b96d89af5e1b0efefed",
           "blockHash":"0xbe2cadb0b88ae723bb2d87293f41c64f3242c12fe7e841f3ecc0698d6e3e9701",
           "blockNumber":"0x160cc3e",
           "data":"0x000000000000000000000000000000000000000000000000bdcaecf562fddac1",
           "logIndex":"0xcf",
           "removed":false,
           "topics":[
              "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
              "0x0000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fad",
              "0x000000000000000000000000f4123c2a38d9aa57801a6469b415f8116208feab"
           ],
           "transactionHash":"0x72823453899fe671cdf0742f5dd3725d3f52e6f65660d31318e327969a850bcb",
           "transactionIndex":"0x24"
        }
     ],
     "logsBloom":"0x00010000000000800000000000010000040000040000000000000000000000000000000000000000000000000000100000010000100020000080400014080000000000080000000800000008000000000000000000080000004000000020000000800000000000001000000000400000000844040000000000000010000800001040004000000000000000000000000002000000000000000000000000000000000008000000000100000000000080000401000020000000000000000000000000000002008000000000000000000000000000000000000000000000000000000000000000000000020000000000000000001000000000000000000002000400",
     "status":"0x1",
     "to":"0x3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad",
     "transactionHash":"0x72823453899fe671cdf0742f5dd3725d3f52e6f65660d31318e327969a850bcb",
     "transactionIndex":"0x24",
     "type":"0x2"
  }
}

我的解码和聚合解决方案如下所示:

import { createPublicClient, http, parseAbiItem, formatUnits, getEventSelector, erc20Abi } from 'viem'
import { base, mainnet } from 'viem/chains'
import { receipt, receiptEight, receiptFive, receiptFour, receiptNine, receiptSeven, receiptSix, receiptTen, receiptThree, receiptTwo } from './receipts.js'

const baseClient = createPublicClient({
  chain: base,
  transport: http()
})

const ethereumClient = createPublicClient({
  chain: mainnet,
  transport: http()
})

// Define transfer event for filtering
const transferEvent = parseAbiItem('event Transfer(address indexed from, address indexed to, uint256 value)')
const transferEventSelector = getEventSelector(transferEvent)

// Add deposit event definition
const depositEvent = parseAbiItem('event Deposit(address indexed dst, uint256 wad)')
const depositEventSelector = getEventSelector(depositEvent)

async function decodeSwap(receipt, network = 'base') {
  const client = network === 'base' ? baseClient : ethereumClient

  const userAddress = receipt.from.toLowerCase()
  let logicPath = ''
  
  // Get all transfer and deposit logs from the receipt
  const transferLogs = receipt.logs.filter(log => 
    log.topics[0] === transferEventSelector
  )
  const depositLogs = receipt.logs.filter(log =>
    log.topics[0] === depositEventSelector
  )

  // Sort transfers by logIndex
  const sortedTransfers = transferLogs.sort((a, b) => 
    Number(a.logIndex) - Number(b.logIndex)
  )

  let sentTransfer, receivedTransfer

  // First try to find direct transfers involving the user
  for (const transfer of sortedTransfers) {
    if (transfer.topics[1].toLowerCase().includes(userAddress.slice(2))) {
      sentTransfer = transfer
      logicPath = 'Direct user transfer found'
      break
    }
  }

  // If no direct user transfer found, check for WETH deposit
  if (!sentTransfer) {
    const wethDeposit = depositLogs[0]
    if (wethDeposit) {
      sentTransfer = wethDeposit
      logicPath = 'WETH deposit found (fallback)'
    }
  }

  // Look for the received transfer after the sent transfer
  if (sentTransfer) {
    const laterTransfers = sortedTransfers.filter(t => 
      Number(t.logIndex) > Number(sentTransfer.logIndex)
    ).reverse()
    
    receivedTransfer = laterTransfers.find(t => 
      t.address.toLowerCase() !== sentTransfer.address.toLowerCase() &&
      t.topics[1].toLowerCase() !== t.topics[2].toLowerCase()
    )

    if (receivedTransfer) {
      logicPath += ' → Different token after sent transfer'
    } else {
      receivedTransfer = laterTransfers.find(t => 
        t.topics[1].toLowerCase() !== t.topics[2].toLowerCase()
      )
      if (receivedTransfer) {
        logicPath += ' → Any valid transfer after sent transfer'
      }
    }
  }

  // If we still haven't found the transfers, look for the first out and last in of different tokens
  if (!sentTransfer || !receivedTransfer) {
    sentTransfer = sortedTransfers.find(t => 
      t.topics[1].toLowerCase() !== t.topics[2].toLowerCase()
    )

    if (sentTransfer) {
      logicPath = 'First valid transfer'
      
      receivedTransfer = [...sortedTransfers].reverse().find(t => 
        t.address.toLowerCase() !== sentTransfer.address.toLowerCase() &&
        t.topics[1].toLowerCase() !== t.topics[2].toLowerCase() &&
        t !== sentTransfer
      )

      if (receivedTransfer) {
        logicPath += ' → Last different token transfer'
      } else {
        receivedTransfer = [...sortedTransfers].reverse().find(t => 
          t.topics[1].toLowerCase() !== t.topics[2].toLowerCase() &&
          t !== sentTransfer
        )
        if (receivedTransfer) {
          logicPath += ' → Last valid transfer'
        }
      }
    }
  }

  if (!receivedTransfer || !sentTransfer) {
    throw new Error('Could not find swap transfers')
  }

  // Get token details
  const [sentToken, receivedToken] = await Promise.all([
    {
      address: sentTransfer.address,
      symbol: await client.readContract({
        address: sentTransfer.address,
        abi: erc20Abi,
        functionName: 'symbol'
      }),
      decimals: await client.readContract({
        address: sentTransfer.address,
        abi: erc20Abi,
        functionName: 'decimals'
      })
    },
    {
      address: receivedTransfer.address,
      symbol: await client.readContract({
        address: receivedTransfer.address,
        abi: erc20Abi,
        functionName: 'symbol'
      }),
      decimals: await client.readContract({
        address: receivedTransfer.address,
        abi: erc20Abi,
        functionName: 'decimals'
      })
    }
  ])

  // Parse the transfer amounts
  const sentAmount = formatUnits(
    BigInt(sentTransfer.data), 
    sentToken.decimals
  )
  const receivedAmount = formatUnits(
    BigInt(receivedTransfer.data),
    receivedToken.decimals
  )

  console.log('Decoded Swap Summary:')
  console.log('--------------------')
  console.log(`Logic Path: ${logicPath}`)
  console.log('\nSwap Details:')
  console.log(`Sent: ${sentAmount} ${sentToken.symbol} (${sentTransfer.address})`)
  console.log(`Received: ${receivedAmount} ${receivedToken.symbol} (${receivedTransfer.address})`)
  console.log('\n')
}

// const receiptOneMain = receipt.receipt

// const receiptTwoMain = receiptTwo.receipt



const receiptThreeMain = receiptThree.receipt
// const receiptFourMain = receiptFour.receipt

// const receiptFiveMain = receiptFive.receipt

// const receiptSixMain = receiptSix.receipt

// const receiptSevenMain = receiptSeven.receipt

// const receiptEightMain = receiptEight.receipt

// const receiptNineMain = receiptNine.receipt

// const receiptTenMain = receiptTen.receipt

// console.log("Receipt One")
// await decodeSwap(receiptOneMain, "base")
// console.log("Receipt Two")
// await decodeSwap(receiptTwoMain, "base")
console.log("Receipt Three")
await decodeSwap(receiptThreeMain, "base")
// console.log("Receipt Four")
// await decodeSwap(receiptFourMain, "base")
// console.log("Receipt Five")
// await decodeSwap(receiptFiveMain, "base")
// console.log("Receipt Six")
// await decodeSwap(receiptSixMain, "base")
// console.log("Receipt Seven")
// await decodeSwap(receiptSevenMain, "base")
// console.log("Receipt Eight")
// await decodeSwap(receiptEightMain, "base")
// console.log("Receipt Nine")
// await decodeSwap(receiptNineMain, "base")
// console.log("Receipt Ten")
// await decodeSwap(receiptTenMain, "ethereum")

运行时,会提供正确的输出,如下所示:

Receipt Three
Decoded Swap Summary:
--------------------
Logic Path: Direct user transfer found → Different token after sent transfer

Swap Details:
Sent: 0.251056 USDT (0xfde4c96c8593536e31f229ea8f37b2ada2699bb2)
Received: 13.676003757135878849 DEGEN (0x4ed4e862860bed51a9570b96d89af5e1b0efefed)

我已经测试了其他 9 个收据,它们来自 UniSwap、1Inch 和其他 3 个 DEX,我的逻辑运行良好并且似乎是正确的,使用不同的后备和策略来获得准确的进出。

我想知道是否有更好的解决方案来获取准确的交换信息(即人类可读的最终结果)?

我的方法有什么缺陷吗?

我应该使用基于 DEX 和版本的 switch-case 方法吗?

如果有人能给我一些指点,那就太好了。

(仅供参考,我已经有一种使用 QuickNode Streams 获取收据的方法,所以不用担心)。

node.js ethereum web3js abi uniswap
1个回答
0
投票

不幸的是,DEX 或其事件没有标准。

  • 您需要单独集成和解码每种 DEX 类型
© www.soinside.com 2019 - 2024. All rights reserved.