@indexing/jiti 0.0.30 → 0.0.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +1049 -817
- package/dist/main.js.map +1 -1
- package/dist/module.js +1049 -817
- package/dist/module.js.map +1 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"AAAA,gBAAuB;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,SAAS,EAAE,CACT,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;QAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,KACjE,OAAO,CAAC;IACb,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB,CAAC;AAEF,aAAoB;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC9D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,oBAA2B;IACzB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,OAAO,EAAE,WAAW,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvD,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AUxBF,OAAA,MAAM,aAAwF,CAAC;AAI/F,8CAEC;AAED,iCAAiC,GAAG,EAAE,MAAM,YAE3C","sources":["src/types.ts","src/templates/raw.ts","src/utils/evm-address-to-checksum.ts","src/utils/evm-chain-to-id.ts","src/utils/evm-method-signature-to-hex.ts","src/utils/evm-decode-log.ts","src/utils/evm-normalize-raw-block.ts","src/utils/index.ts","src/templates/token-transfers.ts","src/templates/index.ts","src/index.ts"],"sourcesContent":["export type Template = {\n key: string;\n name: string;\n description: string;\n disabled: boolean;\n tags: string[];\n params: Param[];\n transform: (\n payload: Record<string, unknown>,\n _ctx?: Record<string, unknown> & { params: Record<string, unknown> }\n ) => unknown;\n tests: TemplateTest[];\n};\n\nexport type Param = {\n key: string;\n name: string;\n type: 'NETWORK' | 'ADDRESS' | 'STRING' | 'NUMBER' | 'BOOLEAN';\n multiple?: boolean;\n optional?: boolean;\n values?: string[];\n default?: string;\n};\n\nexport type TemplateTest = {\n params: Record<string, unknown>;\n payload: `https://${string}` | Record<string, unknown>;\n output: unknown;\n};\n","import { Template } from '../types';\n\nconst rawTemplate: Template = {\n key: 'raw',\n name: 'Raw Block Data',\n description: 'Get all blocks with all available fields',\n tags: ['EVM', 'RAW'],\n disabled: true,\n params: [],\n transform: (payload) => payload,\n tests: [\n {\n params: {},\n payload: { a: 'b', c: 1, d: { e: true } },\n output: { a: 'b', c: 1, d: { e: true } },\n },\n ],\n};\n\nexport default rawTemplate;\n","import { keccak256 } from 'viem';\n\nexport function evmAddressToChecksum(address: string) {\n if (!address || !/^(0x)?[0-9a-f]{40}$/i.test(address)) {\n return null;\n }\n\n const stripAddress = address.slice(2).toLowerCase();\n const keccakHash = keccak256(stripAddress as `0x${string}`);\n let checksumAddress = '0x';\n\n for (let i = 0; i < stripAddress.length; i++) {\n checksumAddress += parseInt(keccakHash[i], 16) >= 8 ? stripAddress[i].toUpperCase() : stripAddress[i];\n }\n\n return checksumAddress;\n}\n","const CHAIN_ID = {\n ARBITRUM: 42161,\n AVALANCHE: 43114,\n BASE: 8453,\n BASE_SEPOLIA: 84532,\n BERACHAIN_BARTIO: 80084,\n BLAST: 81457,\n BSC: 56,\n CELO: 42220,\n CYBER: 7560,\n DEGEN: 666666666,\n ETHEREUM: 1,\n ETH_HOLESKY: 17000,\n ETH_SEPOLIA: 11155111,\n FRAXTAL: 252,\n FRAXTAL_SEPOLIA: 2522,\n FUSE: 122,\n GNOSIS: 100,\n GOLD: 4653,\n HAM: 5112,\n HEMI_TESTNET: 743111,\n LINEA: 59144,\n LINEA_SEPOLIA: 59141,\n LISK_SEPOLIA: 4202,\n LYRA: 957,\n LYRA_SEPOLIA: 901,\n MODE: 34443,\n MODE_SEPOLIA: 919,\n NEON: 245022934,\n OASIS_SAPPHIRE: 23294,\n OPTIMISM: 10,\n OP_SEPOLIA: 11155420,\n ORDERLY: 291,\n ORDERLY_SEPOLIA: 4460,\n PGN_SEPOLIA: 58008,\n POLYGON: 137,\n POLYGON_AMOY: 80002,\n POLYGON_ZKEVM: 1101,\n PUBLIC_GOODS_NETWORK: 424,\n SCROLL: 534352,\n SCROLL_SEPOLIA: 534351,\n STACK: 78225,\n SYNDICATE_FRAME_CHAIN: 5101,\n TRON: 728126428,\n ZER0: 543210,\n ZK_LINK: 810180,\n ZORA: 7777777,\n ZORA_SEPOLIA: 999999999,\n};\n\nexport function evmChainToId(chain: string) {\n return CHAIN_ID[chain?.toUpperCase()];\n}\n","import { keccak256 } from 'viem';\n\nexport function evmMethodSignatureToHex(sig: string): string {\n const [method, rest] = sig.split('(');\n const params = rest\n .split(')')[0]\n .split(',')\n .map((p) => p.trim());\n const topic0 = keccak256(\n `${method.split(' ').pop()}(${params.map((p) => p.split(' ')[0]).join(',')})` as `0x${string}`\n );\n return topic0;\n}\n","import { decodeEventLog, parseAbi } from 'viem';\n\nimport { evmMethodSignatureToHex } from './evm-method-signature-to-hex';\n\ntype BasicLog = Record<string, unknown> & { address: string; topics: string[] };\ntype SignaturesToDecode = string | { addresses?: string[]; signature: string; topic0?: string };\n\nexport function evmDecodeLog(\n log: BasicLog,\n signatures: SignaturesToDecode | SignaturesToDecode[]\n): Record<string, unknown> {\n if (Array.isArray(signatures)) {\n for (const sig of signatures) {\n const decoded = evmDecodeLog(log, sig);\n if (decoded) {\n return decoded;\n }\n }\n\n return null;\n }\n\n if (typeof signatures !== 'string' && Array.isArray(signatures.addresses) && signatures.addresses.length) {\n const addresses = signatures.addresses.map((a) => a?.toLowerCase());\n if (!addresses.includes(log.address.toLowerCase())) {\n return null;\n }\n }\n\n let sig = (typeof signatures === 'string' ? signatures : signatures.signature).trim();\n if (!sig.startsWith('event ')) {\n sig = 'event ' + sig;\n }\n const topic0 = typeof signatures !== 'string' && signatures.topic0 ? signatures.topic0 : evmMethodSignatureToHex(sig);\n if ((sig.match(/ indexed /g)?.length || 0) === log.topics.length - 1) {\n if (log.topics[0] === topic0) {\n try {\n const result = decodeEventLog({\n abi: parseAbi([sig]),\n data: log.data as `0x${string}`,\n topics: log.topics as [],\n });\n return result.args as unknown as Record<string, unknown>;\n } catch (e) {\n // ignore this\n }\n }\n }\n\n return null;\n}\n\nexport function evmDecodeLogWithMetadata(\n log: BasicLog,\n signatures: SignaturesToDecode | SignaturesToDecode[]\n): { decoded: Record<string, unknown>; metadata: { name: string } } {\n for (const sig of [signatures].flat()) {\n const decoded = evmDecodeLog(log, sig);\n if (decoded) {\n return {\n decoded,\n metadata: {\n name: (typeof sig === 'string' ? sig : sig.signature).split('(')[0],\n },\n };\n }\n }\n\n return null;\n}\n","import { evmAddressToChecksum } from './evm-address-to-checksum';\n\nexport function normalizeEVMBlock(rawBlock: Record<string, unknown>): Record<string, unknown> {\n for (const k of ['baseFeePerGas', 'gasLimit', 'gasUsed', 'number', 'size', 'timestamp']) {\n if (typeof rawBlock[k] === 'string') {\n rawBlock[k] = parseInt(rawBlock[k] as string);\n }\n }\n\n for (const k of ['difficulty', 'totalDifficulty']) {\n if (typeof rawBlock[k] === 'string') {\n rawBlock[k] = BigInt(rawBlock[k] as string).toString();\n }\n }\n for (const k of ['miner']) {\n rawBlock[k] = evmAddressToChecksum(rawBlock[k] as string);\n }\n\n rawBlock.transactions = (\n rawBlock.transactions as { receipt: Record<string, unknown>; traces: Record<string, unknown>[] } & Record<\n string,\n unknown\n >[]\n ).map((tx) => {\n for (const k of [\n 'blockNumber',\n 'cumulativeGasUsed',\n 'effectiveGasPrice',\n 'gas',\n 'gasUsed',\n 'nonce',\n 'status',\n 'transactionIndex',\n ]) {\n if (typeof tx[k] === 'string') {\n tx[k] = parseInt(tx[k] as string);\n }\n if (typeof tx.receipt[k] === 'string') {\n tx.receipt[k] = parseInt(tx.receipt[k] as string);\n }\n }\n\n for (const k of ['gasPrice', 'maxFeePerGas', 'maxPriorityFeePerGas', 'value']) {\n if (typeof tx[k] === 'string') {\n tx[k] = BigInt(tx[k] as string).toString();\n }\n if (typeof tx.receipt[k] === 'string') {\n tx.receipt[k] = BigInt(tx.receipt[k] as string).toString();\n }\n }\n\n for (const k of ['from', 'to']) {\n if (typeof tx[k] === 'string') {\n tx[k] = evmAddressToChecksum(tx[k] as string);\n }\n }\n for (const k of ['contractAddress']) {\n if (typeof tx.receipt[k] === 'string') {\n tx.receipt[k] = evmAddressToChecksum(tx.receipt[k] as string);\n }\n }\n\n tx.type = parseInt(tx.type as string);\n tx.receipt['status'] = Boolean(tx.receipt['status'] as string);\n\n tx.receipt['logs'] = (tx.receipt['logs'] as Record<string, unknown>[])?.map((log) => {\n for (const k of ['blockNumber', 'logIndex', 'transactionIndex']) {\n if (typeof log[k] === 'string') {\n log[k] = parseInt(log[k] as string);\n }\n }\n log['address'] = evmAddressToChecksum(log['address'] as string);\n return log;\n });\n\n return tx;\n });\n\n return rawBlock;\n}\n","export * from './evm-address-to-checksum';\nexport * from './evm-chain-to-id';\nexport * from './evm-decode-log';\nexport * from './evm-method-signature-to-hex';\nexport * from './evm-normalize-raw-block';\n","import { evmDecodeLogWithMetadata } from '../utils';\nimport { Template } from '../types';\nimport { decodeTxRaw, Registry } from '@cosmjs/proto-signing';\nimport { defaultRegistryTypes as defaultStargateTypes } from '@cosmjs/stargate';\nimport { sha256 } from 'viem';\n\ntype NetworkTransfer = {\n amount: number | bigint;\n blockNumber: number;\n from: string;\n index?: string;\n timestamp: string;\n to: string;\n token?: string;\n tokenId?: string;\n tokenType: 'NATIVE' | 'TOKEN' | 'NFT';\n transactionGasFee: bigint;\n transactionHash: string;\n};\n\nconst NULL_ADDRESS = '0x0000000000000000000000000000000000000000';\n\nconst tokenTransfersTemplate: Template = {\n key: 'token_transfers',\n name: 'Token Transfers',\n description: 'Get all token transfers for a set of token types.',\n tags: ['EVM', 'ERC20', 'ERC721', 'NFT', 'TOKEN'],\n disabled: false,\n params: [\n { key: 'network', name: 'Network', type: 'NETWORK', optional: false },\n { key: 'contractAddress', name: 'Contract Address', type: 'ADDRESS', optional: true },\n { key: 'walletAddress', name: 'Wallet Address', type: 'ADDRESS', optional: true },\n {\n key: 'tokenTypes',\n name: 'Token Types',\n type: 'STRING',\n multiple: true,\n optional: true,\n values: ['NATIVE', 'TOKEN', 'NFT'],\n },\n ],\n\n transform: (block, _ctx) => {\n const TOKEN_TYPES = (_ctx.params.tokenTypes as NetworkTransfer['tokenType'][]) || [];\n let transfers: NetworkTransfer[] = [];\n\n switch (block._network) {\n case 'APTOS':\n case 'APTOS_TESTNET': {\n for (const tx of block.transactions as Record<string, unknown>[]) {\n if (!tx?.events || !Array.isArray(tx.events)) {\n continue;\n }\n\n const timestamp = tx.timestamp ? new Date(parseInt(tx.timestamp as string, 10) / 1_000).toISOString() : null;\n\n const transfersByKey: Record<\n string,\n {\n amount: string;\n tokenAddress?: string;\n from?: string;\n to?: string;\n }\n > = {};\n\n for (const evt of tx.events as Record<string, unknown>[]) {\n const evtType = evt.type as string;\n if (/::(Withdraw|Deposit)[^:]*/.test(evtType)) {\n const data = evt.data as Record<string, any>;\n const amount = data.amount as string;\n const accountAddr = data.store_owner || (evt.guid as { account_address?: string })?.account_address || '';\n\n let tokenAddr = '0x1::aptos_coin::AptosCoin';\n if (data.store) {\n tokenAddr =\n (\n tx.changes as { address: string; data: { type: string; data: { metadata: { inner: string } } } }[]\n ).find((c) => c.address === data.store && c.data.type === '0x1::fungible_asset::FungibleStore')?.data\n ?.data?.metadata?.inner || data.store;\n }\n\n const compositeKey = `${tx.hash}-${tokenAddr}-${amount}`;\n if (!transfersByKey[compositeKey]) {\n transfersByKey[compositeKey] = {\n amount,\n tokenAddress: tokenAddr,\n };\n }\n\n if (/::Withdraw[^:]*/.test(evtType)) {\n transfersByKey[compositeKey].from = accountAddr;\n } else {\n transfersByKey[compositeKey].to = accountAddr;\n }\n }\n }\n\n for (const partial of Object.values(transfersByKey)) {\n if (!partial.from || !partial.to) {\n continue;\n }\n\n const fromAddr = partial.from.length < 66 ? `0x0${partial.from.slice(2)}` : partial.from;\n const toAddr = partial.to.length < 66 ? `0x0${partial.to.slice(2)}` : partial.to;\n\n let finalToken: string | null = null;\n let finalTokenType: 'NATIVE' | 'TOKEN' | 'NFT' = 'TOKEN';\n\n if (partial.tokenAddress?.toLowerCase().includes('aptos_coin')) {\n finalToken = null;\n } else {\n finalToken = partial.tokenAddress?.toLowerCase();\n }\n\n const gasUsed = BigInt((tx.gas_used as string) || '0');\n\n transfers.push({\n amount: BigInt(partial.amount),\n blockNumber: parseInt(block.block_height as string, 10),\n from: fromAddr,\n to: toAddr,\n timestamp,\n token: finalToken,\n tokenType: finalTokenType,\n transactionGasFee: gasUsed,\n transactionHash: tx.hash as string,\n });\n }\n }\n break;\n }\n\n case 'BITCOIN':\n case 'BITCOIN_TESTNET':\n case 'LITCOIN': // @TODO\n case 'DOGECOIN': {\n for (const tx of block.tx as Record<string, unknown>[]) {\n const timestamp = tx.time ? new Date((tx.time as number) * 1000).toISOString() : null;\n const vin = tx.vin[0] as { prevout?: { scriptPubKey: { address: string } }; vout?: number };\n const vout = tx.vout as { value: number; scriptPubKey?: { address: string; addresses?: string[] } }[];\n\n const fromVout = Math.min(vin.vout || 1000, vout.length - 1);\n const fromAddress =\n vin.prevout?.scriptPubKey?.address ||\n vout[fromVout]?.scriptPubKey?.address ||\n vout[fromVout]?.scriptPubKey?.addresses?.[0];\n if (!fromAddress) {\n continue;\n }\n\n for (const v of vout) {\n transfers.push({\n amount: BigInt(Math.round(v.value * Math.pow(10, 8))),\n blockNumber: block.height as number,\n from: fromAddress,\n timestamp,\n to: v.scriptPubKey.address || v.scriptPubKey.addresses?.[0],\n transactionGasFee: BigInt(Math.round(((tx.fee as number) || 0) * Math.pow(10, 8))),\n transactionHash: tx.txid as string,\n token: null,\n tokenType: 'NATIVE',\n });\n }\n }\n break;\n }\n\n case 'CARDANO': {\n const blockTimestamp = new Date((block.timestamp as number) * 1000).toISOString();\n\n for (const tx of block.transactions as unknown[]) {\n const typedTx = tx as {\n transaction_identifier?: { hash?: string };\n operations?: {\n type: string;\n account?: { address?: string };\n amount?: {\n value?: string;\n currency?: {\n symbol?: string;\n decimals?: number;\n };\n };\n }[];\n };\n\n if (!Array.isArray(typedTx.operations)) {\n continue;\n }\n\n const transactionHash = typedTx.transaction_identifier?.hash || '';\n\n const inputs = typedTx.operations.filter((op) => op.type === 'input');\n const outputs = typedTx.operations.filter((op) => op.type === 'output');\n\n if (!inputs.length && !outputs.length) {\n continue;\n }\n\n const fromAddress = inputs[0]?.account?.address;\n if (!fromAddress) {\n continue;\n }\n\n const sumInputs = inputs.reduce((acc, op) => {\n const val = BigInt(op.amount?.value || '0');\n return acc + val;\n }, BigInt(0));\n const sumOutputs = outputs.reduce((acc, op) => {\n const val = BigInt(op.amount?.value || '0');\n return acc + val;\n }, BigInt(0));\n\n const transactionFee = sumInputs + BigInt(sumOutputs);\n\n for (const out of outputs) {\n const rawValue = out.amount?.value || '0';\n const absoluteValue = BigInt(rawValue);\n transfers.push({\n amount: absoluteValue < 0 ? -absoluteValue : absoluteValue,\n blockNumber: (block.block_identifier as { index: number }).index,\n from: fromAddress,\n timestamp: blockTimestamp,\n to: out.account?.address || '',\n token: out.amount?.currency?.symbol?.toUpperCase() === 'ADA' ? null : out.amount?.currency?.symbol,\n tokenType: out.amount?.currency?.symbol?.toUpperCase() === 'ADA' ? 'NATIVE' : 'TOKEN',\n transactionGasFee: transactionFee < 0 ? -transactionFee : transactionFee,\n transactionHash,\n });\n }\n }\n break;\n }\n\n case 'RIPPLE': {\n if (!Array.isArray(block.transactions)) {\n break;\n }\n\n for (const rawTx of block.transactions) {\n const typedTx = rawTx as {\n Account?: string;\n Amount?: string | { currency: string; issuer: string; value: string };\n Destination?: string;\n Fee?: string;\n hash?: string;\n TransactionType?: string;\n date?: number;\n metaData?: {\n delivered_amount?: string | { currency: string; issuer: string; value: string };\n };\n };\n if (typedTx.TransactionType !== 'Payment') {\n continue;\n }\n const deliveredOrAmount = typedTx.metaData?.delivered_amount ?? typedTx.Amount ?? '0';\n let tokenSymbol = 'XRP';\n let tokenType: 'NATIVE' | 'TOKEN' | 'NFT' = 'NATIVE';\n\n let parsedAmount: bigint;\n\n if (typeof deliveredOrAmount === 'object') {\n tokenSymbol = deliveredOrAmount.currency?.toUpperCase() ?? 'UNKNOWN';\n tokenType = 'TOKEN';\n const floatVal = parseFloat(deliveredOrAmount.value);\n const smallestUnit = Math.round(floatVal * 1_000_000);\n parsedAmount = BigInt(smallestUnit);\n } else {\n parsedAmount = BigInt(String(deliveredOrAmount));\n }\n transfers.push({\n amount: parsedAmount,\n blockNumber: parseInt(block.ledger_index as string, 10),\n from: typedTx.Account ?? 'UNKNOWN',\n timestamp: block.close_time_iso ? (block.close_time_iso as string) : null,\n to: typedTx.Destination ?? 'UNKNOWN',\n token: tokenSymbol,\n tokenType: tokenType,\n transactionGasFee: BigInt(typedTx.Fee ?? '0'),\n transactionHash: typedTx.hash ?? '',\n });\n }\n\n break;\n }\n\n case 'SOLANA': {\n for (const tx of block.transactions as unknown[]) {\n const solanaTx = tx as {\n meta: {\n fee: number;\n postTokenBalances: {\n accountIndex: number;\n mint: string;\n owner: string;\n uiTokenAmount: { amount: string };\n }[];\n preTokenBalances: {\n accountIndex: number;\n mint: string;\n owner: string;\n uiTokenAmount: { amount: string };\n }[];\n postBalances: number[];\n preBalances: number[];\n };\n transaction: {\n message: { accountKeys: (string | { pubkey: string })[]; instructions: unknown[] };\n signatures: string[];\n };\n };\n const txHash = solanaTx.transaction.signatures[0];\n const timestamp = block.blockTime ? new Date((block.blockTime as number) * 1000).toISOString() : null;\n\n let txFee = BigInt(solanaTx.meta.fee);\n if (txFee < BigInt(10)) {\n txFee = txFee * BigInt(Math.pow(10, 9));\n }\n\n const transfersByKey: Record<string, NetworkTransfer> = {};\n for (const post of solanaTx.meta.postTokenBalances) {\n let matched = false;\n for (const pre of solanaTx.meta.preTokenBalances) {\n if (post.mint === pre.mint && post.owner === pre.owner) {\n let diff = BigInt(post.uiTokenAmount.amount) - BigInt(pre.uiTokenAmount.amount);\n if (diff === BigInt(0)) {\n continue;\n }\n const isNegDiff = diff < 0;\n if (diff < 0) {\n diff = -diff;\n }\n\n const key = `${post.mint}-${diff.toString()}`;\n const txfer: NetworkTransfer = {\n amount: diff,\n blockNumber: block.blockHeight as number,\n from: pre.owner,\n timestamp,\n to: post.owner,\n transactionGasFee: txFee,\n transactionHash: txHash,\n token: post.mint,\n tokenType: 'TOKEN',\n };\n if (transfersByKey[key]) {\n if (isNegDiff) {\n delete txfer.to;\n } else {\n delete txfer.from;\n }\n }\n transfersByKey[key] = Object.assign(transfersByKey[key] || {}, txfer);\n matched = true;\n }\n }\n\n if (!matched) {\n let diff = BigInt(post.uiTokenAmount.amount);\n const isNegDiff = diff < 0;\n if (diff < 0) {\n diff = -diff;\n }\n const key = `${post.mint}-${diff.toString()}`;\n const txfer: NetworkTransfer = {\n amount: diff,\n blockNumber: block.blockHeight as number,\n from: null,\n timestamp,\n to: post.owner,\n token: post.mint,\n tokenType: 'TOKEN',\n transactionGasFee: txFee,\n transactionHash: txHash,\n };\n if (transfersByKey[key]) {\n if (isNegDiff) {\n delete txfer.to;\n } else {\n delete txfer.from;\n }\n }\n transfersByKey[key] = Object.assign(transfersByKey[key] || {}, txfer);\n }\n }\n\n for (let i = 1; i < solanaTx.meta.postBalances.length; i += 1) {\n const post = solanaTx.meta.postBalances[i];\n const pre = solanaTx.meta.preBalances[i];\n if (post !== undefined && pre !== undefined && post !== pre) {\n let diff = BigInt(post) - BigInt(pre);\n if (diff < 0) {\n diff = -diff;\n }\n const key = `null-${diff.toString()}`;\n\n const txfer: NetworkTransfer = {\n amount: diff,\n blockNumber: block.blockHeight as number,\n from:\n post > pre\n ? typeof solanaTx.transaction.message.accountKeys[0] === 'string'\n ? solanaTx.transaction.message.accountKeys[0]\n : (solanaTx.transaction.message.accountKeys[0] as { pubkey: string })?.pubkey\n : typeof solanaTx.transaction.message.accountKeys[i] === 'string'\n ? (solanaTx.transaction.message.accountKeys[i] as string)\n : (solanaTx.transaction.message.accountKeys[i] as { pubkey: string })?.pubkey,\n timestamp,\n to:\n typeof solanaTx.transaction.message.accountKeys[i] === 'string'\n ? (solanaTx.transaction.message.accountKeys[i] as string)\n : (solanaTx.transaction.message.accountKeys[i] as { pubkey: string })?.pubkey?.toString(),\n token: null,\n tokenType: 'NATIVE',\n transactionGasFee: txFee,\n transactionHash: txHash,\n };\n if (transfersByKey[key]) {\n if (post > pre) {\n delete txfer.from;\n } else {\n delete txfer.to;\n }\n }\n transfersByKey[key] = Object.assign(transfersByKey[key] || {}, txfer);\n }\n }\n\n transfers.push(...Object.values(transfersByKey));\n }\n break;\n }\n\n case 'STARKNET': {\n if (!Array.isArray(block.transactions)) {\n break;\n }\n\n for (const tx of block.transactions) {\n const typedTx = tx as {\n transaction_hash: string;\n sender_address: string;\n receipt?: {\n actual_fee?: {\n amount?: string;\n unit?: string;\n };\n events?: {\n keys: string[];\n data: string[];\n }[];\n };\n timestamp?: number | string;\n data_availability?: Record<string, unknown>;\n };\n\n const timestamp = block.timestamp ? new Date((block.timestamp as number) * 1000).toISOString() : null;\n\n let transactionGasFee = BigInt(0);\n if (typedTx?.receipt?.actual_fee?.amount) {\n transactionGasFee = BigInt(typedTx.receipt.actual_fee.amount);\n }\n\n const transactionHash = typedTx.transaction_hash;\n\n if (!typedTx.receipt?.events) {\n continue;\n }\n\n for (const event of typedTx.receipt.events) {\n if (!event.keys.includes('0x99cd8bde557814842a3121e8ddfd433a539b8c9f14bf31ebf108d12e6196e9')) {\n continue;\n }\n if (event.data.length < 3) {\n continue;\n }\n\n const [from, to, amountHex] = event.data;\n const amount = BigInt(amountHex);\n\n transfers.push({\n amount,\n blockNumber: block.block_number as number,\n from,\n timestamp,\n to,\n token: null,\n tokenType: 'NATIVE',\n transactionGasFee,\n transactionHash,\n });\n }\n }\n break;\n }\n\n case 'STELLAR': {\n for (const tx of block.transactions as unknown[]) {\n const typedTx = tx as {\n hash: string;\n created_at: string;\n fee_charged: string;\n operations: {\n type: string;\n from: string;\n to: string;\n amount: string;\n asset_type: string;\n asset_issuer: string;\n }[];\n };\n for (const op of typedTx.operations) {\n if (op.type === 'payment') {\n transfers.push({\n amount: BigInt(op.amount.replace('.', '')),\n blockNumber: block.sequence as number,\n from: op.from,\n timestamp: typedTx.created_at,\n to: op.to,\n token: op.asset_type === 'native' ? null : op.asset_issuer,\n tokenType: op.asset_type === 'native' ? 'NATIVE' : 'TOKEN',\n transactionGasFee: BigInt(typedTx.fee_charged),\n transactionHash: typedTx.hash,\n });\n }\n }\n }\n break;\n }\n\n case 'SUI': {\n const blockNumber = parseInt(block.sequence as string, 10);\n const blockTimestamp = new Date(parseInt(block.timestamp as string, 10)).toISOString();\n\n const transactions = (block.transactions as any[]) || [];\n for (const tx of transactions) {\n const transactionHash = tx.digest as string;\n\n let transactionGasFee = BigInt(0);\n if (tx.effects?.gasUsed) {\n const gu = tx.effects.gasUsed;\n transactionGasFee =\n BigInt(gu.computationCost) +\n BigInt(gu.storageCost) -\n BigInt(gu.storageRebate) +\n BigInt(gu.nonRefundableStorageFee);\n if (transactionGasFee < 0n) {\n transactionGasFee = 0n;\n }\n }\n const balanceChanges = (tx.balanceChanges as any[]) || [];\n for (const bc of balanceChanges) {\n let rawAmt = BigInt(bc.amount as string);\n if (rawAmt === 0n) continue;\n\n let fromAddr: string | undefined;\n let toAddr: string | undefined;\n\n const rawOwner =\n bc.owner?.AddressOwner ||\n bc.owner?.ObjectOwner ||\n bc.owner?.Shared?.initial_shared_version ||\n 'UNKNOWN_OWNER';\n\n if (rawAmt < 0n) {\n fromAddr = String(rawOwner);\n toAddr = undefined;\n rawAmt = -rawAmt;\n } else {\n fromAddr = undefined;\n toAddr = String(rawOwner);\n }\n\n transfers.push({\n blockNumber,\n from: fromAddr,\n to: toAddr,\n amount: rawAmt,\n token: bc.coinType as string,\n tokenType: 'NATIVE',\n timestamp: blockTimestamp,\n transactionHash,\n transactionGasFee,\n });\n }\n }\n break;\n }\n\n case 'TON': {\n const blockNumber = block.seqno as number;\n const blockTimestamp = new Date((block.shards?.[0]?.gen_utime as number) * 1000).toISOString();\n\n for (const shard of (block.shards as any[]) || []) {\n for (const tx of (shard.transactions as any[]) || []) {\n const transactionLT = tx.transaction_id.lt as string;\n const transactionHash = tx.transaction_id.hash as string;\n const transactionFee = BigInt((tx.fee as string) || '0');\n\n const inVal = BigInt((tx.in_msg?.value as string) || '0');\n if (inVal > 0n) {\n transfers.push({\n blockNumber,\n from: tx.in_msg?.source?.account_address as string,\n to: tx.address?.account_address as string,\n amount: inVal,\n token: 'TON',\n tokenType: 'NATIVE',\n timestamp: blockTimestamp,\n transactionHash,\n transactionGasFee: transactionFee,\n });\n }\n\n for (const outMsg of (tx.out_msgs as any[]) || []) {\n const outVal = BigInt((outMsg.value as string) || '0');\n if (outVal > 0n) {\n transfers.push({\n blockNumber,\n from: outMsg.source?.account_address as string,\n to: outMsg.destination?.account_address as string,\n amount: outVal,\n token: 'TON',\n tokenType: 'NATIVE',\n timestamp: blockTimestamp,\n transactionHash,\n transactionGasFee: transactionFee,\n });\n }\n }\n }\n }\n\n break;\n }\n\n case 'ASTAR':\n case 'ENJIN':\n case 'KUSAMA':\n case 'POLKADOT':\n case 'BITTENSOR': {\n const typedBlock = block as {\n blockNumber: number;\n header: { number: string };\n extrinsics: {\n method: string;\n signer: string;\n args: any[];\n hash: string;\n }[];\n };\n\n const blockNumber = typedBlock.blockNumber;\n\n const timestampExtrinsic = typedBlock.extrinsics.find((ex) => ex.method === 'timestamp.set');\n const blockTimestamp = timestampExtrinsic\n ? new Date(Number(timestampExtrinsic.args[0].toString().replace(/,/g, ''))).toISOString()\n : new Date().toISOString();\n\n for (const extrinsic of typedBlock.extrinsics) {\n if (['balances.transfer', 'balances.transferKeepAlive'].includes(extrinsic.method)) {\n const from = extrinsic.signer;\n const to = (extrinsic.args[0] as { Id?: string })?.Id || '';\n const amount = BigInt((extrinsic.args[1] as string).replace(/,/g, ''));\n\n transfers.push({\n amount,\n blockNumber,\n from,\n to,\n token: null,\n tokenType: 'NATIVE',\n timestamp: blockTimestamp,\n transactionGasFee: 0n,\n transactionHash: extrinsic.hash,\n });\n }\n }\n break;\n }\n\n case 'FILECOIN': {\n const typedBlock = block as {\n Height: number;\n Blocks: Array<{ ParentBaseFee: string; Timestamp: number }>;\n messages: Array<{\n blockMessages: {\n BlsMessages?: Array<unknown>;\n SecpkMessages?: Array<{\n Message: {\n From: string;\n To: string;\n Value: string;\n GasFeeCap: string;\n GasPremium: string;\n };\n CID: { '/': string };\n }>;\n };\n }>;\n receipts: Array<{ GasUsed: number }>;\n };\n\n const blockNumber = typedBlock.Height;\n const blockTimestamp = new Date(typedBlock.Blocks[0].Timestamp * 1000).toISOString();\n const parentBaseFee = BigInt(typedBlock.Blocks[0].ParentBaseFee);\n\n let receiptIndex = 0;\n\n for (const msgGroup of typedBlock.messages) {\n const secpkMessages = msgGroup.blockMessages.SecpkMessages || [];\n\n for (const msg of secpkMessages) {\n const receipt = typedBlock.receipts[receiptIndex++];\n const gasUsed = receipt ? BigInt(receipt.GasUsed) : BigInt(0);\n const gasFeeCap = BigInt(msg.Message.GasFeeCap);\n const gasPremium = BigInt(msg.Message.GasPremium);\n const baseFeeBurn = gasUsed * parentBaseFee;\n const minerTip =\n gasUsed * (gasPremium < gasFeeCap - parentBaseFee ? gasPremium : gasFeeCap - parentBaseFee);\n const transactionGasFee = baseFeeBurn + minerTip;\n\n transfers.push({\n amount: BigInt(msg.Message.Value),\n blockNumber,\n from: msg.Message.From,\n to: msg.Message.To,\n token: null,\n tokenType: 'NATIVE',\n timestamp: blockTimestamp,\n transactionGasFee,\n transactionHash: msg.CID['/'],\n });\n }\n }\n break;\n }\n\n // attempt to introspect data types\n default: {\n // try Cosmos\n if (block.block) {\n const typedBlock = block as {\n block: { header: { height: string; time: string }; data: { txs?: string[] } };\n block_id: { hash: string };\n };\n\n const blockNumber = Number(typedBlock.block.header.height);\n const blockTimestamp = new Date(typedBlock.block.header.time).toISOString();\n\n for (const txRaw of typedBlock.block.data.txs || []) {\n const decoded = decodeTxRaw(new Uint8Array(Buffer.from(txRaw, 'base64')));\n const txHash = sha256(new Uint8Array(Buffer.from(txRaw, 'base64')));\n const transactionGasFee = BigInt(decoded.authInfo.fee?.amount?.[0]?.amount || '0');\n\n const registry = new Registry(defaultStargateTypes);\n for (const message of decoded.body.messages) {\n if (\n ['/ibc.applications.transfer.v1.MsgTransfer', '/cosmos.bank.v1beta1.MsgSend'].includes(message.typeUrl)\n ) {\n const decodedMsg = registry.decode(message);\n transfers.push({\n blockNumber,\n from: decodedMsg.sender,\n to: decodedMsg.receiver,\n amount: BigInt(decodedMsg.token.amount),\n token: decodedMsg.token.denom,\n tokenType: 'NATIVE',\n timestamp: blockTimestamp,\n transactionHash: txHash.slice(2).toUpperCase(),\n transactionGasFee,\n });\n }\n }\n }\n\n break;\n }\n\n // otherwise assume EVM\n for (const tx of block.transactions as any[]) {\n if (!tx.receipt) {\n continue;\n }\n\n const timestamp = new Date((block.timestamp as number) * 1000).toISOString();\n const transactionGasFee = BigInt(tx.receipt.gasUsed) * BigInt(tx.receipt.effectiveGasPrice);\n\n // track direct ETH transfers\n if (!TOKEN_TYPES.length || TOKEN_TYPES.includes('NATIVE')) {\n // pull from traces, if available\n if (Array.isArray(tx.traces)) {\n for (const trace of tx.traces.filter((t) => t.action)) {\n const action = trace.action as unknown as { from: string; to: string; value: string };\n if (!action?.value) continue;\n\n transfers.push({\n amount: BigInt(action.value),\n blockNumber: tx.blockNumber as number,\n from: action.from?.toLowerCase() || NULL_ADDRESS,\n index: trace.traceAddress?.join('-'),\n timestamp,\n to: action.to?.toLowerCase() || NULL_ADDRESS,\n tokenType: 'NATIVE',\n transactionGasFee,\n transactionHash: tx.hash,\n });\n }\n } else if ((tx.value as string)?.length >= 3 || /\\d+/.test(tx.value as string)) {\n transfers.push({\n amount: BigInt(tx.value as string),\n blockNumber: tx.blockNumber as number,\n from: tx.from?.toLowerCase() || NULL_ADDRESS,\n timestamp,\n to: tx.to?.toLowerCase() || NULL_ADDRESS,\n tokenType: 'NATIVE',\n transactionGasFee,\n transactionHash: tx.hash,\n });\n }\n }\n\n // track ERC20 transfers\n if (!TOKEN_TYPES.length || TOKEN_TYPES.includes('TOKEN')) {\n for (const log of tx.receipt.logs) {\n const txfer = evmDecodeLogWithMetadata(log, [\n 'Transfer(address indexed from, address indexed to, uint256 value)',\n ]);\n if (txfer) {\n transfers.push({\n amount: txfer.decoded.value as bigint,\n blockNumber: tx.blockNumber as number,\n from: (txfer.decoded.from as string)?.toLowerCase() || NULL_ADDRESS,\n index: log.logIndex,\n timestamp,\n to: (txfer.decoded.to as string)?.toLowerCase() || NULL_ADDRESS,\n token: (log.address as string).toLowerCase(),\n tokenType: 'TOKEN',\n transactionGasFee,\n transactionHash: tx.hash,\n });\n }\n }\n }\n\n if (!TOKEN_TYPES.length || TOKEN_TYPES.includes('NFT')) {\n for (const log of tx.receipt.logs) {\n const txfer = evmDecodeLogWithMetadata(log, [\n 'Transfer(address indexed from, address indexed to, uint256 indexed value)',\n 'TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value)',\n 'TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values)',\n ]);\n\n if (!txfer) continue;\n\n switch (txfer.metadata.name) {\n case 'Transfer': {\n transfers.push({\n amount: 1,\n blockNumber: tx.blockNumber as number,\n from: (txfer.decoded.from as string)?.toLowerCase() || NULL_ADDRESS,\n index: log.logIndex,\n timestamp,\n to: (txfer.decoded.to as string)?.toLowerCase() || NULL_ADDRESS,\n token: (log.address as string).toLowerCase(),\n tokenId: (txfer.decoded.value as bigint).toString(),\n tokenType: 'NFT',\n transactionGasFee,\n transactionHash: tx.hash,\n });\n break;\n }\n case 'TransferSingle': {\n transfers.push({\n amount: txfer.decoded.value as bigint,\n blockNumber: tx.blockNumber as number,\n from: (txfer.decoded.from as string)?.toLowerCase() || NULL_ADDRESS,\n index: log.logIndex,\n timestamp,\n to: (txfer.decoded.to as string)?.toLowerCase() || NULL_ADDRESS,\n token: (log.address as string).toLowerCase(),\n tokenId: (txfer.decoded.id as bigint).toString(),\n tokenType: 'NFT',\n transactionGasFee,\n transactionHash: tx.hash,\n });\n break;\n }\n case 'TransferBatch': {\n for (let i = 0; i < (txfer.decoded.ids as bigint[]).length; i += 1) {\n transfers.push({\n amount: txfer.decoded.values[i] as bigint,\n blockNumber: tx.blockNumber as number,\n from: (txfer.decoded.from as string)?.toLowerCase() || NULL_ADDRESS,\n index: log.logIndex,\n timestamp,\n to: (txfer.decoded.to as string)?.toLowerCase() || NULL_ADDRESS,\n token: (log.address as string).toLowerCase(),\n tokenId: (txfer.decoded.ids[i] as bigint).toString(),\n tokenType: 'NFT',\n transactionGasFee,\n transactionHash: tx.hash,\n });\n }\n break;\n }\n }\n }\n }\n }\n if (typeof _ctx.params.contractAddress === 'string') {\n _ctx.params.contractAddress = _ctx.params.contractAddress.toLowerCase();\n }\n if (typeof _ctx.params.walletAddress === 'string') {\n _ctx.params.walletAddress = _ctx.params.walletAddress.toLowerCase();\n }\n break;\n }\n }\n\n const seenTransfers = new Set<string>();\n\n transfers = transfers.filter((txfer) => {\n if (txfer.amount <= BigInt(0)) {\n return false;\n }\n if (_ctx.params.contractAddress && _ctx.params.contractAddress !== txfer.token && txfer.token) {\n return false;\n }\n if (_ctx.params.walletAddress && ![txfer.from, txfer.to].includes(_ctx.params.walletAddress as string)) {\n return false;\n }\n\n const key = `${txfer.transactionHash}-${txfer.from}-${txfer.to}-${txfer.amount}-${txfer.token}`;\n\n if (seenTransfers.has(key)) {\n return false;\n }\n\n seenTransfers.add(key);\n\n return true;\n });\n\n return transfers;\n },\n\n tests: [\n // APTOS\n {\n params: {\n network: 'APTOS',\n walletAddress: '0x5bd7de5c56d5691f32ea86c973c73fec7b1445e59736c97158020018c080bb00',\n contractAddress: '0x3b5d2e7e8da86903beb19d5a7135764aac812e18af193895d75f3a8f6a066cb0',\n },\n payload: 'https://jiti.indexing.co/networks/aptos/297956660',\n output: [\n {\n amount: 1611839920n,\n blockNumber: 297956660,\n from: '0x5bd7de5c56d5691f32ea86c973c73fec7b1445e59736c97158020018c080bb00',\n to: '0x3b5d2e7e8da86903beb19d5a7135764aac812e18af193895d75f3a8f6a066cb0',\n timestamp: '2025-03-02T21:07:06.002Z',\n token: null,\n tokenType: 'TOKEN',\n transactionGasFee: 13n,\n transactionHash: '0xfbdef795d11df124cca264f3370b09fb04fb1c1d24a2d2e1df0693c096a76d13',\n },\n {\n amount: 1502138836n,\n blockNumber: 297956660,\n from: '0x5bd7de5c56d5691f32ea86c973c73fec7b1445e59736c97158020018c080bb00',\n to: '0x04b2b6bc8c2c5794c51607c962f482593f9b5ea09373a8ce249a1f799cca7a1e',\n timestamp: '2025-03-02T21:07:06.002Z',\n token: null,\n tokenType: 'TOKEN',\n transactionGasFee: 13n,\n transactionHash: '0xfbdef795d11df124cca264f3370b09fb04fb1c1d24a2d2e1df0693c096a76d13',\n },\n ],\n },\n\n // APTOS\n {\n params: {\n network: 'APTOS',\n contractAddress: '0xbae207659db88bea0cbead6da0ed00aac12edcdda169e591cd41c94180b46f3b',\n },\n payload: 'https://jiti.indexing.co/networks/aptos/303623631',\n output: [\n {\n amount: 1000060n,\n blockNumber: 303623631,\n from: '0xa4e7455d27731ab857e9701b1e6ed72591132b909fe6e4fd99b66c1d6318d9e8',\n timestamp: '2025-03-14T15:39:49.845Z',\n to: '0x9317336bfc9ba6987d40492ddea8d41e11b7c2e473f3556a9c82309d326e79ce',\n token: '0xbae207659db88bea0cbead6da0ed00aac12edcdda169e591cd41c94180b46f3b',\n tokenType: 'TOKEN',\n transactionGasFee: 16n,\n transactionHash: '0x24b8854bad1f6543b35069eacd6ec40a583ca7fa452b422b04d747d24b65279c',\n },\n ],\n },\n\n // BASE\n {\n params: {\n network: 'BASE',\n walletAddress: '0x4F80864cD68782144e3736626896990acAe15a11',\n contractAddress: '0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed',\n },\n payload: 'https://jiti.indexing.co/networks/base/23507423',\n output: [\n {\n amount: 1000000000000000000000n,\n blockNumber: 23507423,\n from: '0x053002b4b332b422733c9469ddf9990bb6235e3d',\n index: 536,\n timestamp: '2024-12-10T04:16:33.000Z',\n to: '0x4f80864cd68782144e3736626896990acae15a11',\n token: '0x4ed4e862860bed51a9570b96d89af5e1b0efefed',\n tokenType: 'TOKEN',\n transactionGasFee: 1192354854229n,\n transactionHash: '0x69c9b12ccbe2d4f2f1dfc7c4a8557fc099fc5df276424417815acbc79a06fd56',\n },\n ],\n },\n\n // BITTENSOR\n {\n params: {\n network: 'BITTENSOR',\n walletAddress: '5G6WmJ4mym9oSQzUF5tr7LvsNMvH5vWtutZM8vMvRv1Wwy6J',\n contractAddress: '',\n },\n payload: 'https://jiti.indexing.co/networks/bittensor/2652896',\n output: [\n {\n amount: 2154999850n,\n blockNumber: 2652896,\n from: '5G6WmJ4mym9oSQzUF5tr7LvsNMvH5vWtutZM8vMvRv1Wwy6J',\n to: '5CFwmfLfL1Z6vXU6hgGksh6irDFpcVXaTTNRoqDGMyHrDKK1',\n token: null,\n tokenType: 'NATIVE',\n timestamp: '2024-03-28T12:53:36.001Z',\n transactionGasFee: 0n,\n transactionHash: '0x9ea55a8f40b8d7704f27964155476ab9c744ec2ef2b1f431ad0e05a3a6f3c1ab',\n },\n ],\n },\n\n // CARDANO\n {\n params: {\n network: 'CARDANO',\n walletAddress:\n 'addr1q9syxu908lef7r6rsvk0h7gsx3rxj22cuykgx2a2l4hcfd8e9y2e9vtv4w9dyej96w99wwj8hwgc273862lk6a3vt30qjjrund',\n contractAddress: '',\n },\n payload: 'https://jiti.indexing.co/networks/cardano/11443286',\n output: [\n {\n amount: 1110000n,\n blockNumber: 11443286,\n from: 'addr1qymdv285few5tyqvya86rl97r9e608njs37shfew6l2nn473aw2pcnrcvfwfgg2dnew99m4tjj0apsu7232w2euzwpysndh0h3',\n timestamp: '+057068-01-19T05:23:20.000Z',\n to: 'addr1q9syxu908lef7r6rsvk0h7gsx3rxj22cuykgx2a2l4hcfd8e9y2e9vtv4w9dyej96w99wwj8hwgc273862lk6a3vt30qjjrund',\n token: null,\n tokenType: 'NATIVE',\n transactionGasFee: 174257n,\n transactionHash: '261c42ba9124f55d8e169ebb692cd3759d796a54369acb316ee449b546e79309',\n },\n ],\n },\n\n // COSMOS\n {\n params: {\n network: 'COSMOS',\n walletAddress: 'cosmos1x4qvmtcfc02pklttfgxzdccxcsyzklrxavteyz',\n contractAddress: 'ibc/F663521BF1836B00F5F177680F74BFB9A8B5654A694D0D2BC249E03CF2509013',\n },\n payload: 'https://jiti.indexing.co/networks/cosmos/24419691',\n output: [\n {\n blockNumber: 24419691,\n from: 'cosmos1x4qvmtcfc02pklttfgxzdccxcsyzklrxavteyz',\n to: 'noble1x4qvmtcfc02pklttfgxzdccxcsyzklrx4073uv',\n amount: 500000n,\n token: 'ibc/F663521BF1836B00F5F177680F74BFB9A8B5654A694D0D2BC249E03CF2509013',\n tokenType: 'NATIVE',\n timestamp: '2025-02-14T21:48:22.809Z',\n transactionHash: '963D4D7BB59C1280F58A7ECA2F1934E2AA005109A989193C815C7B98EDCD7445',\n transactionGasFee: 4860n,\n },\n ],\n },\n\n // DOGECOIN\n {\n params: {\n network: 'DOGECOIN',\n walletAddress: 'DMqRVLrhbam3Kcfddpxd6EYvEBbpi3bEpP',\n contractAddress: '',\n },\n payload: 'https://jiti.indexing.co/networks/dogecoin/1000075',\n output: [\n {\n amount: 1008521000000n,\n blockNumber: 1000075,\n from: 'DMqRVLrhbam3Kcfddpxd6EYvEBbpi3bEpP',\n to: 'DMqRVLrhbam3Kcfddpxd6EYvEBbpi3bEpP',\n token: null,\n tokenType: 'NATIVE',\n transactionGasFee: 0n,\n transactionHash: '9873fe46ab29f61cefdec498b691af68e0ad29a7599c94f42d2d4e9a5d461dbe',\n timestamp: '2015-12-13T19:59:52.000Z',\n },\n ],\n },\n\n // FILECOIN\n {\n params: {\n network: 'FILECOIN',\n walletAddress: 'f1e3aa3z6gkaqxxwmbbna5gf2frggswwjaeavx7bq',\n contractAddress: 'f1bqdligg7ipuiizvmdn7ijobhbkwaieh6z6lah5y',\n },\n payload: 'https://jiti.indexing.co/networks/filecoin/4818438',\n output: [\n {\n amount: 7896300000000000000n,\n blockNumber: 4818438,\n from: 'f1e3aa3z6gkaqxxwmbbna5gf2frggswwjaeavx7bq',\n timestamp: '2025-03-24T23:39:00.000Z',\n to: 'f1bqdligg7ipuiizvmdn7ijobhbkwaieh6z6lah5y',\n token: null,\n tokenType: 'NATIVE',\n transactionGasFee: 1592498365133760n,\n transactionHash: 'bafy2bzacecxud3tayyq3caagjej5srufcx5fufjuqkz3ltgfty27wdsrmqeew',\n },\n ],\n },\n\n // RIPPLE\n {\n params: {\n network: 'RIPPLE',\n walletAddress: 'rUUgoiJmjTPEbxfZ4RsS9pVS9Kv813Wpui',\n contractAddress: '',\n },\n payload: 'https://jiti.indexing.co/networks/ripple/88104659',\n output: [\n {\n amount: 238n,\n blockNumber: 88104659,\n from: 'rMAGnTv4eMWktZnhKa5cHcDiY84ZiKUaQm',\n timestamp: '2024-05-19T22:18:52Z',\n to: 'rUUgoiJmjTPEbxfZ4RsS9pVS9Kv813Wpui',\n token: 'XRP',\n tokenType: 'NATIVE',\n transactionGasFee: 15n,\n transactionHash: '03564E6109261CDE73FCC5062C2A0A70F365CB1A0F9408C065B60EC3E94E4DBF',\n },\n ],\n },\n\n // STELLAR\n {\n params: {\n network: 'STELLAR',\n walletAddress: 'GA5KLTNAWV27IOTBX5PKUOMVWFMLX4X7CPMQJ4QLR3G266MMVL7NMA4X',\n contractAddress: 'GC4Z2TDXU4GXVLHOS5P5SU6HKBCP7NKN4TJ5ZGTVRBW7MCBZTU7SNUSA',\n },\n payload: 'https://jiti.indexing.co/networks/stellar/51720546',\n output: [\n {\n amount: 150000n,\n blockNumber: 51720546,\n from: 'GA5KLTNAWV27IOTBX5PKUOMVWFMLX4X7CPMQJ4QLR3G266MMVL7NMA4X',\n timestamp: '2024-05-18T04:41:39Z',\n to: 'GC4Z2TDXU4GXVLHOS5P5SU6HKBCP7NKN4TJ5ZGTVRBW7MCBZTU7SNUSA',\n token: 'GC4Z2TDXU4GXVLHOS5P5SU6HKBCP7NKN4TJ5ZGTVRBW7MCBZTU7SNUSA',\n tokenType: 'TOKEN',\n transactionGasFee: 100n,\n transactionHash: '4fb2441210cbe87f5003abdfa86f03bafa54f789ed041feccbda0bd054297c4d',\n },\n ],\n },\n\n // STARKNET\n {\n params: {\n network: 'STARKNET',\n walletAddress: '0x309e6b209031362268d62d646a067365e6f6d6eb7f571b5212cbdfd5f26fe54',\n contractAddress: '',\n },\n payload: 'https://jiti.indexing.co/networks/starknet/1149460',\n output: [\n {\n amount: 0x1c286f74458fc6n,\n blockNumber: 1149460,\n from: '0x309e6b209031362268d62d646a067365e6f6d6eb7f571b5212cbdfd5f26fe54',\n timestamp: '2025-02-13T17:36:52.000Z',\n to: '0x1176a1bd84444c89232ec27754698e5d2e7e1a7f1539f12027f28b23ec9f3d8',\n token: null,\n tokenType: 'NATIVE',\n transactionGasFee: 7925758505095110n,\n transactionHash: '0x707203dba31f442ae9a5477e6a8906f3676effa0f1d3bb19cbbc14e1ddfe21',\n },\n ],\n },\n // SUI\n {\n params: {\n network: 'SUI',\n walletAddress: '0x39b9e5942df9a4686ebe727534077281d6adcee15a9bb74d3e052e56d78b2744',\n contractAddress: '',\n },\n payload: 'https://jiti.indexing.co/networks/sui/132734364',\n output: [\n {\n blockNumber: 132734364,\n from: '0x39b9e5942df9a4686ebe727534077281d6adcee15a9bb74d3e052e56d78b2744',\n to: undefined,\n amount: 5321172n,\n token: '0x2::sui::SUI',\n tokenType: 'NATIVE',\n timestamp: '2025-04-11T14:57:40.091Z',\n transactionHash: '4JWC8DX8eKYhwvRgzesGNWsb5t5RUyQVSNibKxLRaN22',\n transactionGasFee: 5408344n,\n },\n {\n blockNumber: 132734364,\n from: '0x39b9e5942df9a4686ebe727534077281d6adcee15a9bb74d3e052e56d78b2744',\n to: undefined,\n amount: 825772n,\n token: '0x2::sui::SUI',\n tokenType: 'NATIVE',\n timestamp: '2025-04-11T14:57:40.091Z',\n transactionHash: '5bXBDeoYqXTawkf2bCDriAE2M6Vu2EgYKEgJ1hW4qagZ',\n transactionGasFee: 901544n,\n },\n {\n blockNumber: 132734364,\n from: '0x39b9e5942df9a4686ebe727534077281d6adcee15a9bb74d3e052e56d78b2744',\n to: undefined,\n amount: 5321248n,\n token: '0x2::sui::SUI',\n tokenType: 'NATIVE',\n timestamp: '2025-04-11T14:57:40.091Z',\n transactionHash: '6KZYwvaAk76AL9p5kjkA27Tj5Jy47ipCxLKiCXtBbngo',\n transactionGasFee: 5408496n,\n },\n {\n blockNumber: 132734364,\n from: '0x39b9e5942df9a4686ebe727534077281d6adcee15a9bb74d3e052e56d78b2744',\n to: undefined,\n amount: 5321096n,\n token: '0x2::sui::SUI',\n tokenType: 'NATIVE',\n timestamp: '2025-04-11T14:57:40.091Z',\n transactionHash: 'HcJAHtSUypt8z2us2HqDm7PzB5HKS3ASY86pDKNAsgnE',\n transactionGasFee: 5408192n,\n },\n ],\n },\n // TON\n {\n params: {\n network: 'TON',\n walletAddress: 'EQAFukUyzmHjUvOYDOjNE-wbZFFl2FWas1rFJoh8IiTsWD40',\n contractAddress: '',\n },\n payload: 'https://jiti.indexing.co/networks/ton/44919328',\n output: [\n {\n blockNumber: 44919328,\n from: 'EQAFukUyzmHjUvOYDOjNE-wbZFFl2FWas1rFJoh8IiTsWD40',\n to: 'EQCFTFAHOU3vFt2NiZhRD5dwuS0k7GS59vIg3WfCKwfaQGW2',\n amount: 10000000n,\n token: 'TON',\n tokenType: 'NATIVE',\n timestamp: '2025-02-13T23:10:18.000Z',\n transactionHash: 'Vh5cWr2uvCsdhoouBQ+EiUcF54os9oqvh8A/62EroQc=',\n transactionGasFee: 2355233n,\n },\n ],\n },\n\n // SOLANA\n {\n params: {\n network: 'SOLANA',\n walletAddress: 'D89hHJT5Aqyx1trP6EnGY9jJUB3whgnq3aUvvCqedvzf',\n contractAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',\n },\n payload: 'https://jiti.indexing.co/networks/solana/326286476',\n output: [\n {\n amount: 3000350n,\n blockNumber: 304546603,\n from: 'D89hHJT5Aqyx1trP6EnGY9jJUB3whgnq3aUvvCqedvzf',\n timestamp: '2025-03-12T14:42:16.000Z',\n to: 'HTd5J9YhYnN1nwCAQiykpNBjoDrgtPVUcpk9TBPMCV4b',\n transactionGasFee: 105000n,\n transactionHash: '3HKaqRRPyA2NHvmf3xzJpgTemxXcextCRmgwWfHpKhDqDbJSqCdy8GtH5zG8tHQU2Dcznf7JgMP7sCLQSoNPw2E5',\n token: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',\n tokenType: 'TOKEN',\n },\n ],\n },\n {\n params: {\n network: 'SOLANA',\n walletAddress: '5u3gUxSsiqddQf1QhRDRQqDaFe9f5S3pyVkVWy77gB85',\n contractAddress: '',\n },\n payload: 'https://jiti.indexing.co/networks/solana/291562718',\n output: [\n {\n amount: 239999n,\n blockNumber: 270452291,\n from: '5u3gUxSsiqddQf1QhRDRQqDaFe9f5S3pyVkVWy77gB85',\n timestamp: '2024-09-23T16:45:56.000Z',\n to: 'CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM',\n token: null,\n tokenType: 'NATIVE',\n transactionGasFee: 45000n,\n transactionHash: '2TKq9VXjQocvyQGraxqRjyF8iASRPCmjJcGXkxYQbTknjFTToVB2rRSLMFCi5VwBx9uJ2fAN1YNt4Wr8TUVRuoMi',\n },\n {\n amount: 1n,\n blockNumber: 270452291,\n from: '5u3gUxSsiqddQf1QhRDRQqDaFe9f5S3pyVkVWy77gB85',\n timestamp: '2024-09-23T16:45:56.000Z',\n to: '8MCjBNEBEyT5uAnDBupUxQ8eiKHJcf4vh546g4uM7cjF',\n token: null,\n tokenType: 'NATIVE',\n transactionGasFee: 45000n,\n transactionHash: '2TKq9VXjQocvyQGraxqRjyF8iASRPCmjJcGXkxYQbTknjFTToVB2rRSLMFCi5VwBx9uJ2fAN1YNt4Wr8TUVRuoMi',\n },\n ],\n },\n {\n params: {\n network: 'SOLANA',\n walletAddress: '5cuy7pMhTPhVZN9xuhgSbykRb986siGJb6vnEtkuBrSU',\n contractAddress: '27G8MtK7VtTcCHkpASjSDdkWWYfoqT6ggEuKidVJidD4',\n },\n payload: 'https://jiti.indexing.co/networks/solana/325076237',\n output: [\n {\n amount: 14216129n,\n blockNumber: 303338729,\n from: '8KbrpeSRYXYjWSSdG7gE1tR7Go8MmKKxKaei1gGc4U7Q',\n timestamp: '2025-03-07T01:16:59.000Z',\n to: '5cuy7pMhTPhVZN9xuhgSbykRb986siGJb6vnEtkuBrSU',\n transactionGasFee: 353096n,\n transactionHash: '54qvCYcmUvPX6K3KuKG1nGRJWq6696LxwPNsx7DX5rcRsLbFntGEuWCjHTVc5wMcUZhKs1MuXeqpuDswrvNjETNQ',\n token: '27G8MtK7VtTcCHkpASjSDdkWWYfoqT6ggEuKidVJidD4',\n tokenType: 'TOKEN',\n },\n ],\n },\n ],\n};\n\nexport default tokenTransfersTemplate;\n","import rawTemplate from './raw';\nimport tokenTransfersTemplate from './token-transfers';\n\nexport const templates = [rawTemplate, tokenTransfersTemplate];\n","import { templates as templateList } from './templates';\nimport * as utils from './utils';\nimport * as types from './types';\n\nconst templates = templateList.reduce((a, b) => ({ ...a, [b.key]: Object.assign({}, b) }), {});\n\nexport { utils, templates, types };\n\nexport function getAllTemplates() {\n return templateList.slice();\n}\n\nexport function getTemplateByKey(key: string) {\n return templateList.slice().find((template) => template.key === key);\n}\n"],"names":[],"version":3,"file":"types.d.ts.map"}
|
|
1
|
+
{"mappings":"AAAA,gBAAuB;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,SAAS,EAAE,CACT,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;QAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,KACjE,OAAO,CAAC;IACb,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB,CAAC;AAEF,aAAoB;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC9D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,oBAA2B;IACzB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,OAAO,EAAE,WAAW,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvD,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AwBxBF,OAAA,MAAM,aAAwF,CAAC;AAI/F,8CAEC;AAED,iCAAiC,GAAG,EAAE,MAAM,YAE3C","sources":["src/types.ts","src/templates/raw.ts","src/templates/token-transfers/types.ts","src/templates/token-transfers/aptos.ts","src/templates/token-transfers/cardano.ts","src/templates/token-transfers/cosmos.ts","src/utils/evm-address-to-checksum.ts","src/utils/evm-chain-to-id.ts","src/utils/evm-method-signature-to-hex.ts","src/utils/evm-decode-log.ts","src/utils/evm-normalize-raw-block.ts","src/utils/index.ts","src/templates/token-transfers/evm.ts","src/templates/token-transfers/filecoin.ts","src/templates/token-transfers/ripple.ts","src/templates/token-transfers/sui.ts","src/templates/token-transfers/solana.ts","src/templates/token-transfers/starknet.ts","src/templates/token-transfers/stellar.ts","src/templates/token-transfers/substrate.ts","src/templates/token-transfers/ton.ts","src/templates/token-transfers/utxo.ts","src/templates/token-transfers/index.ts","src/templates/index.ts","src/index.ts"],"sourcesContent":["export type Template = {\n key: string;\n name: string;\n description: string;\n disabled: boolean;\n tags: string[];\n params: Param[];\n transform: (\n payload: Record<string, unknown>,\n _ctx?: Record<string, unknown> & { params: Record<string, unknown> }\n ) => unknown;\n tests: TemplateTest[];\n};\n\nexport type Param = {\n key: string;\n name: string;\n type: 'NETWORK' | 'ADDRESS' | 'STRING' | 'NUMBER' | 'BOOLEAN';\n multiple?: boolean;\n optional?: boolean;\n values?: string[];\n default?: string;\n};\n\nexport type TemplateTest = {\n params: Record<string, unknown>;\n payload: `https://${string}` | Record<string, unknown>;\n output: unknown;\n};\n\nexport type SubTemplate = {\n match: (payload: Record<string, unknown>) => boolean;\n transform: Template['transform'];\n tests: Template['tests'];\n};\n","import { Template } from '../types';\n\nconst rawTemplate: Template = {\n key: 'raw',\n name: 'Raw Block Data',\n description: 'Get all blocks with all available fields',\n tags: ['EVM', 'RAW'],\n disabled: true,\n params: [],\n transform: (payload) => payload,\n tests: [\n {\n params: {},\n payload: { a: 'b', c: 1, d: { e: true } },\n output: { a: 'b', c: 1, d: { e: true } },\n },\n ],\n};\n\nexport default rawTemplate;\n","export type NetworkTransfer = {\n amount: number | bigint;\n blockNumber: number;\n from: string;\n index?: string;\n timestamp: string;\n to: string;\n token?: string;\n tokenId?: string;\n tokenType: 'NATIVE' | 'TOKEN' | 'NFT';\n transactionGasFee: bigint;\n transactionHash: string;\n};\n","import { SubTemplate } from '../../types';\nimport { NetworkTransfer } from './types';\n\nexport const AptosTokenTransfers: SubTemplate = {\n match: (block) => ['APTOS', 'APTOS_TESTNET'].includes(block._network as string),\n\n transform(block) {\n let transfers: NetworkTransfer[] = [];\n\n for (const tx of block.transactions as Record<string, unknown>[]) {\n if (!tx?.events || !Array.isArray(tx.events)) {\n continue;\n }\n\n const timestamp = tx.timestamp ? new Date(parseInt(tx.timestamp as string, 10) / 1_000).toISOString() : null;\n\n const transfersByKey: Record<\n string,\n {\n amount: string;\n tokenAddress?: string;\n from?: string;\n to?: string;\n }\n > = {};\n\n for (const evt of tx.events as Record<string, unknown>[]) {\n const evtType = evt.type as string;\n if (/::(Withdraw|Deposit)[^:]*/.test(evtType)) {\n const data = evt.data as Record<string, any>;\n const amount = data.amount as string;\n const accountAddr = data.store_owner || (evt.guid as { account_address?: string })?.account_address || '';\n\n let tokenAddr = '0x1::aptos_coin::AptosCoin';\n if (data.store) {\n tokenAddr =\n (tx.changes as { address: string; data: { type: string; data: { metadata: { inner: string } } } }[]).find(\n (c) => c.address === data.store && c.data.type === '0x1::fungible_asset::FungibleStore'\n )?.data?.data?.metadata?.inner || data.store;\n }\n\n const compositeKey = `${tx.hash}-${tokenAddr}-${amount}`;\n if (!transfersByKey[compositeKey]) {\n transfersByKey[compositeKey] = {\n amount,\n tokenAddress: tokenAddr,\n };\n }\n\n if (/::Withdraw[^:]*/.test(evtType)) {\n transfersByKey[compositeKey].from = accountAddr;\n } else {\n transfersByKey[compositeKey].to = accountAddr;\n }\n }\n }\n\n for (const partial of Object.values(transfersByKey)) {\n if (!partial.from || !partial.to) {\n continue;\n }\n\n const fromAddr = partial.from.length < 66 ? `0x0${partial.from.slice(2)}` : partial.from;\n const toAddr = partial.to.length < 66 ? `0x0${partial.to.slice(2)}` : partial.to;\n\n let finalToken: string | null = null;\n let finalTokenType: 'NATIVE' | 'TOKEN' | 'NFT' = 'TOKEN';\n\n if (partial.tokenAddress?.toLowerCase().includes('aptos_coin')) {\n finalToken = null;\n } else {\n finalToken = partial.tokenAddress?.toLowerCase();\n }\n\n const gasUsed = BigInt((tx.gas_used as string) || '0');\n\n transfers.push({\n amount: BigInt(partial.amount),\n blockNumber: parseInt(block.block_height as string, 10),\n from: fromAddr,\n to: toAddr,\n timestamp,\n token: finalToken,\n tokenType: finalTokenType,\n transactionGasFee: gasUsed,\n transactionHash: tx.hash as string,\n });\n }\n }\n\n return transfers;\n },\n\n tests: [\n {\n params: {\n network: 'APTOS',\n walletAddress: '0x5bd7de5c56d5691f32ea86c973c73fec7b1445e59736c97158020018c080bb00',\n },\n payload: 'https://jiti.indexing.co/networks/aptos/297956660',\n output: [\n {\n amount: 1611839920n,\n blockNumber: 297956660,\n from: '0x5bd7de5c56d5691f32ea86c973c73fec7b1445e59736c97158020018c080bb00',\n to: '0x3b5d2e7e8da86903beb19d5a7135764aac812e18af193895d75f3a8f6a066cb0',\n timestamp: '2025-03-02T21:07:06.002Z',\n token: null,\n tokenType: 'TOKEN',\n transactionGasFee: 13n,\n transactionHash: '0xfbdef795d11df124cca264f3370b09fb04fb1c1d24a2d2e1df0693c096a76d13',\n },\n {\n amount: 1502138836n,\n blockNumber: 297956660,\n from: '0x5bd7de5c56d5691f32ea86c973c73fec7b1445e59736c97158020018c080bb00',\n to: '0x04b2b6bc8c2c5794c51607c962f482593f9b5ea09373a8ce249a1f799cca7a1e',\n timestamp: '2025-03-02T21:07:06.002Z',\n token: null,\n tokenType: 'TOKEN',\n transactionGasFee: 13n,\n transactionHash: '0xfbdef795d11df124cca264f3370b09fb04fb1c1d24a2d2e1df0693c096a76d13',\n },\n ],\n },\n {\n params: {\n network: 'APTOS',\n contractAddress: '0xbae207659db88bea0cbead6da0ed00aac12edcdda169e591cd41c94180b46f3b',\n },\n payload: 'https://jiti.indexing.co/networks/aptos/303623631',\n output: [\n {\n amount: 1000060n,\n blockNumber: 303623631,\n from: '0xa4e7455d27731ab857e9701b1e6ed72591132b909fe6e4fd99b66c1d6318d9e8',\n timestamp: '2025-03-14T15:39:49.845Z',\n to: '0x9317336bfc9ba6987d40492ddea8d41e11b7c2e473f3556a9c82309d326e79ce',\n token: '0xbae207659db88bea0cbead6da0ed00aac12edcdda169e591cd41c94180b46f3b',\n tokenType: 'TOKEN',\n transactionGasFee: 16n,\n transactionHash: '0x24b8854bad1f6543b35069eacd6ec40a583ca7fa452b422b04d747d24b65279c',\n },\n ],\n },\n ],\n};\n","import { SubTemplate } from '../../types';\nimport { NetworkTransfer } from './types';\n\nexport const CardanoTokenTransfers: SubTemplate = {\n match: (block) => ['CARDANO'].includes(block._network as string),\n\n transform(block) {\n let transfers: NetworkTransfer[] = [];\n\n const blockTimestamp = new Date((block.timestamp as number) * 1000).toISOString();\n\n for (const tx of block.transactions as unknown[]) {\n const typedTx = tx as {\n transaction_identifier?: { hash?: string };\n operations?: {\n type: string;\n account?: { address?: string };\n amount?: {\n value?: string;\n currency?: {\n symbol?: string;\n decimals?: number;\n };\n };\n }[];\n };\n\n if (!Array.isArray(typedTx.operations)) {\n continue;\n }\n\n const transactionHash = typedTx.transaction_identifier?.hash || '';\n\n const inputs = typedTx.operations.filter((op) => op.type === 'input');\n const outputs = typedTx.operations.filter((op) => op.type === 'output');\n\n if (!inputs.length && !outputs.length) {\n continue;\n }\n\n const fromAddress = inputs[0]?.account?.address;\n if (!fromAddress) {\n continue;\n }\n\n const sumInputs = inputs.reduce((acc, op) => {\n const val = BigInt(op.amount?.value || '0');\n return acc + val;\n }, BigInt(0));\n const sumOutputs = outputs.reduce((acc, op) => {\n const val = BigInt(op.amount?.value || '0');\n return acc + val;\n }, BigInt(0));\n\n const transactionFee = sumInputs + BigInt(sumOutputs);\n\n for (const out of outputs) {\n const rawValue = out.amount?.value || '0';\n const absoluteValue = BigInt(rawValue);\n transfers.push({\n amount: absoluteValue < 0 ? -absoluteValue : absoluteValue,\n blockNumber: (block.block_identifier as { index: number }).index,\n from: fromAddress,\n timestamp: blockTimestamp,\n to: out.account?.address || '',\n token: out.amount?.currency?.symbol?.toUpperCase() === 'ADA' ? null : out.amount?.currency?.symbol,\n tokenType: out.amount?.currency?.symbol?.toUpperCase() === 'ADA' ? 'NATIVE' : 'TOKEN',\n transactionGasFee: transactionFee < 0 ? -transactionFee : transactionFee,\n transactionHash,\n });\n }\n }\n\n return transfers;\n },\n\n tests: [\n {\n params: {\n network: 'CARDANO',\n walletAddress:\n 'addr1q9syxu908lef7r6rsvk0h7gsx3rxj22cuykgx2a2l4hcfd8e9y2e9vtv4w9dyej96w99wwj8hwgc273862lk6a3vt30qjjrund',\n contractAddress: '',\n },\n payload: 'https://jiti.indexing.co/networks/cardano/11443286',\n output: [\n {\n amount: 1110000n,\n blockNumber: 11443286,\n from: 'addr1qymdv285few5tyqvya86rl97r9e608njs37shfew6l2nn473aw2pcnrcvfwfgg2dnew99m4tjj0apsu7232w2euzwpysndh0h3',\n timestamp: '+057068-01-19T05:23:20.000Z',\n to: 'addr1q9syxu908lef7r6rsvk0h7gsx3rxj22cuykgx2a2l4hcfd8e9y2e9vtv4w9dyej96w99wwj8hwgc273862lk6a3vt30qjjrund',\n token: null,\n tokenType: 'NATIVE',\n transactionGasFee: 174257n,\n transactionHash: '261c42ba9124f55d8e169ebb692cd3759d796a54369acb316ee449b546e79309',\n },\n ],\n },\n ],\n};\n","import { decodeTxRaw, Registry } from '@cosmjs/proto-signing';\nimport { defaultRegistryTypes as defaultStargateTypes } from '@cosmjs/stargate';\nimport { sha256 } from 'viem';\n\nimport { SubTemplate } from '../../types';\nimport { NetworkTransfer } from './types';\n\nexport const CosmosTokenTransfers: SubTemplate = {\n match: (block) => !!block.block,\n\n transform(block) {\n let transfers: NetworkTransfer[] = [];\n\n const typedBlock = block as {\n block: { header: { height: string; time: string }; data: { txs?: string[] } };\n block_id: { hash: string };\n };\n\n const blockNumber = Number(typedBlock.block.header.height);\n const blockTimestamp = new Date(typedBlock.block.header.time).toISOString();\n\n for (const txRaw of typedBlock.block.data.txs || []) {\n const decoded = decodeTxRaw(new Uint8Array(Buffer.from(txRaw, 'base64')));\n const txHash = sha256(new Uint8Array(Buffer.from(txRaw, 'base64')));\n const transactionGasFee = BigInt(decoded.authInfo.fee?.amount?.[0]?.amount || '0');\n\n const registry = new Registry(defaultStargateTypes);\n for (const message of decoded.body.messages) {\n if (['/ibc.applications.transfer.v1.MsgTransfer', '/cosmos.bank.v1beta1.MsgSend'].includes(message.typeUrl)) {\n const decodedMsg = registry.decode(message);\n transfers.push({\n blockNumber,\n from: decodedMsg.sender,\n to: decodedMsg.receiver,\n amount: BigInt(decodedMsg.token.amount),\n token: decodedMsg.token.denom,\n tokenType: 'NATIVE',\n timestamp: blockTimestamp,\n transactionHash: txHash.slice(2).toUpperCase(),\n transactionGasFee,\n });\n }\n }\n }\n\n return transfers;\n },\n\n tests: [\n {\n params: {\n network: 'COSMOS',\n walletAddress: 'cosmos1x4qvmtcfc02pklttfgxzdccxcsyzklrxavteyz',\n contractAddress: 'ibc/F663521BF1836B00F5F177680F74BFB9A8B5654A694D0D2BC249E03CF2509013',\n },\n payload: 'https://jiti.indexing.co/networks/cosmos/24419691',\n output: [\n {\n blockNumber: 24419691,\n from: 'cosmos1x4qvmtcfc02pklttfgxzdccxcsyzklrxavteyz',\n to: 'noble1x4qvmtcfc02pklttfgxzdccxcsyzklrx4073uv',\n amount: 500000n,\n token: 'ibc/F663521BF1836B00F5F177680F74BFB9A8B5654A694D0D2BC249E03CF2509013',\n tokenType: 'NATIVE',\n timestamp: '2025-02-14T21:48:22.809Z',\n transactionHash: '963D4D7BB59C1280F58A7ECA2F1934E2AA005109A989193C815C7B98EDCD7445',\n transactionGasFee: 4860n,\n },\n ],\n },\n ],\n};\n","import { keccak256 } from 'viem';\n\nexport function evmAddressToChecksum(address: string) {\n if (!address || !/^(0x)?[0-9a-f]{40}$/i.test(address)) {\n return null;\n }\n\n const stripAddress = address.slice(2).toLowerCase();\n const keccakHash = keccak256(stripAddress as `0x${string}`);\n let checksumAddress = '0x';\n\n for (let i = 0; i < stripAddress.length; i++) {\n checksumAddress += parseInt(keccakHash[i], 16) >= 8 ? stripAddress[i].toUpperCase() : stripAddress[i];\n }\n\n return checksumAddress;\n}\n","const CHAIN_ID = {\n ARBITRUM: 42161,\n AVALANCHE: 43114,\n BASE: 8453,\n BASE_SEPOLIA: 84532,\n BERACHAIN_BARTIO: 80084,\n BLAST: 81457,\n BSC: 56,\n CELO: 42220,\n CYBER: 7560,\n DEGEN: 666666666,\n ETHEREUM: 1,\n ETH_HOLESKY: 17000,\n ETH_SEPOLIA: 11155111,\n FRAXTAL: 252,\n FRAXTAL_SEPOLIA: 2522,\n FUSE: 122,\n GNOSIS: 100,\n GOLD: 4653,\n HAM: 5112,\n HEMI_TESTNET: 743111,\n LINEA: 59144,\n LINEA_SEPOLIA: 59141,\n LISK_SEPOLIA: 4202,\n LYRA: 957,\n LYRA_SEPOLIA: 901,\n MODE: 34443,\n MODE_SEPOLIA: 919,\n NEON: 245022934,\n OASIS_SAPPHIRE: 23294,\n OPTIMISM: 10,\n OP_SEPOLIA: 11155420,\n ORDERLY: 291,\n ORDERLY_SEPOLIA: 4460,\n PGN_SEPOLIA: 58008,\n POLYGON: 137,\n POLYGON_AMOY: 80002,\n POLYGON_ZKEVM: 1101,\n PUBLIC_GOODS_NETWORK: 424,\n SCROLL: 534352,\n SCROLL_SEPOLIA: 534351,\n STACK: 78225,\n SYNDICATE_FRAME_CHAIN: 5101,\n TRON: 728126428,\n ZER0: 543210,\n ZK_LINK: 810180,\n ZORA: 7777777,\n ZORA_SEPOLIA: 999999999,\n};\n\nexport function evmChainToId(chain: string) {\n return CHAIN_ID[chain?.toUpperCase()];\n}\n","import { keccak256 } from 'viem';\n\nexport function evmMethodSignatureToHex(sig: string): string {\n const [method, rest] = sig.split('(');\n const params = rest\n .split(')')[0]\n .split(',')\n .map((p) => p.trim());\n const topic0 = keccak256(\n `${method.split(' ').pop()}(${params.map((p) => p.split(' ')[0]).join(',')})` as `0x${string}`\n );\n return topic0;\n}\n","import { decodeEventLog, parseAbi } from 'viem';\n\nimport { evmMethodSignatureToHex } from './evm-method-signature-to-hex';\n\ntype BasicLog = Record<string, unknown> & { address: string; topics: string[] };\ntype SignaturesToDecode = string | { addresses?: string[]; signature: string; topic0?: string };\n\nexport function evmDecodeLog(\n log: BasicLog,\n signatures: SignaturesToDecode | SignaturesToDecode[]\n): Record<string, unknown> {\n if (Array.isArray(signatures)) {\n for (const sig of signatures) {\n const decoded = evmDecodeLog(log, sig);\n if (decoded) {\n return decoded;\n }\n }\n\n return null;\n }\n\n if (typeof signatures !== 'string' && Array.isArray(signatures.addresses) && signatures.addresses.length) {\n const addresses = signatures.addresses.map((a) => a?.toLowerCase());\n if (!addresses.includes(log.address.toLowerCase())) {\n return null;\n }\n }\n\n let sig = (typeof signatures === 'string' ? signatures : signatures.signature).trim();\n if (!sig.startsWith('event ')) {\n sig = 'event ' + sig;\n }\n const topic0 = typeof signatures !== 'string' && signatures.topic0 ? signatures.topic0 : evmMethodSignatureToHex(sig);\n if ((sig.match(/ indexed /g)?.length || 0) === log.topics.length - 1) {\n if (log.topics[0] === topic0) {\n try {\n const result = decodeEventLog({\n abi: parseAbi([sig]),\n data: log.data as `0x${string}`,\n topics: log.topics as [],\n });\n return result.args as unknown as Record<string, unknown>;\n } catch (e) {\n // ignore this\n }\n }\n }\n\n return null;\n}\n\nexport function evmDecodeLogWithMetadata(\n log: BasicLog,\n signatures: SignaturesToDecode | SignaturesToDecode[]\n): { decoded: Record<string, unknown>; metadata: { name: string } } {\n for (const sig of [signatures].flat()) {\n const decoded = evmDecodeLog(log, sig);\n if (decoded) {\n return {\n decoded,\n metadata: {\n name: (typeof sig === 'string' ? sig : sig.signature).split('(')[0],\n },\n };\n }\n }\n\n return null;\n}\n","import { evmAddressToChecksum } from './evm-address-to-checksum';\n\nexport function normalizeEVMBlock(rawBlock: Record<string, unknown>): Record<string, unknown> {\n for (const k of ['baseFeePerGas', 'gasLimit', 'gasUsed', 'number', 'size', 'timestamp']) {\n if (typeof rawBlock[k] === 'string') {\n rawBlock[k] = parseInt(rawBlock[k] as string);\n }\n }\n\n for (const k of ['difficulty', 'totalDifficulty']) {\n if (typeof rawBlock[k] === 'string') {\n rawBlock[k] = BigInt(rawBlock[k] as string).toString();\n }\n }\n for (const k of ['miner']) {\n rawBlock[k] = evmAddressToChecksum(rawBlock[k] as string);\n }\n\n rawBlock.transactions = (\n rawBlock.transactions as { receipt: Record<string, unknown>; traces: Record<string, unknown>[] } & Record<\n string,\n unknown\n >[]\n ).map((tx) => {\n for (const k of [\n 'blockNumber',\n 'cumulativeGasUsed',\n 'effectiveGasPrice',\n 'gas',\n 'gasUsed',\n 'nonce',\n 'status',\n 'transactionIndex',\n ]) {\n if (typeof tx[k] === 'string') {\n tx[k] = parseInt(tx[k] as string);\n }\n if (typeof tx.receipt[k] === 'string') {\n tx.receipt[k] = parseInt(tx.receipt[k] as string);\n }\n }\n\n for (const k of ['gasPrice', 'maxFeePerGas', 'maxPriorityFeePerGas', 'value']) {\n if (typeof tx[k] === 'string') {\n tx[k] = BigInt(tx[k] as string).toString();\n }\n if (typeof tx.receipt[k] === 'string') {\n tx.receipt[k] = BigInt(tx.receipt[k] as string).toString();\n }\n }\n\n for (const k of ['from', 'to']) {\n if (typeof tx[k] === 'string') {\n tx[k] = evmAddressToChecksum(tx[k] as string);\n }\n }\n for (const k of ['contractAddress']) {\n if (typeof tx.receipt[k] === 'string') {\n tx.receipt[k] = evmAddressToChecksum(tx.receipt[k] as string);\n }\n }\n\n tx.type = parseInt(tx.type as string);\n tx.receipt['status'] = Boolean(tx.receipt['status'] as string);\n\n tx.receipt['logs'] = (tx.receipt['logs'] as Record<string, unknown>[])?.map((log) => {\n for (const k of ['blockNumber', 'logIndex', 'transactionIndex']) {\n if (typeof log[k] === 'string') {\n log[k] = parseInt(log[k] as string);\n }\n }\n log['address'] = evmAddressToChecksum(log['address'] as string);\n return log;\n });\n\n return tx;\n });\n\n return rawBlock;\n}\n","export * from './evm-address-to-checksum';\nexport * from './evm-chain-to-id';\nexport * from './evm-decode-log';\nexport * from './evm-method-signature-to-hex';\nexport * from './evm-normalize-raw-block';\n","import { SubTemplate } from '../../types';\nimport { NetworkTransfer } from './types';\nimport { evmDecodeLogWithMetadata } from '../../utils';\n\nconst NULL_ADDRESS = '0x0000000000000000000000000000000000000000';\n\nexport const EVMTokenTransfers: SubTemplate = {\n match: (block) => true,\n\n transform(block, _ctx) {\n const TOKEN_TYPES = (_ctx.params.tokenTypes as NetworkTransfer['tokenType'][]) || [];\n let transfers: NetworkTransfer[] = [];\n\n for (const tx of block.transactions as any[]) {\n if (!tx.receipt) {\n continue;\n }\n\n const timestamp = new Date((block.timestamp as number) * 1000).toISOString();\n const transactionGasFee = BigInt(tx.receipt.gasUsed) * BigInt(tx.receipt.effectiveGasPrice);\n\n // track direct ETH transfers\n if (!TOKEN_TYPES.length || TOKEN_TYPES.includes('NATIVE')) {\n // pull from traces, if available\n if (Array.isArray(tx.traces)) {\n for (const trace of tx.traces.filter((t) => t.action)) {\n const action = trace.action as unknown as { from: string; to: string; value: string };\n if (!action?.value) continue;\n\n transfers.push({\n amount: BigInt(action.value),\n blockNumber: tx.blockNumber as number,\n from: action.from?.toLowerCase() || NULL_ADDRESS,\n index: trace.traceAddress?.join('-'),\n timestamp,\n to: action.to?.toLowerCase() || NULL_ADDRESS,\n tokenType: 'NATIVE',\n transactionGasFee,\n transactionHash: tx.hash,\n });\n }\n } else if ((tx.value as string)?.length >= 3 || /\\d+/.test(tx.value as string)) {\n transfers.push({\n amount: BigInt(tx.value as string),\n blockNumber: tx.blockNumber as number,\n from: tx.from?.toLowerCase() || NULL_ADDRESS,\n timestamp,\n to: tx.to?.toLowerCase() || NULL_ADDRESS,\n tokenType: 'NATIVE',\n transactionGasFee,\n transactionHash: tx.hash,\n });\n }\n }\n\n // track ERC20 transfers\n if (!TOKEN_TYPES.length || TOKEN_TYPES.includes('TOKEN')) {\n for (const log of tx.receipt.logs) {\n const txfer = evmDecodeLogWithMetadata(log, [\n 'Transfer(address indexed from, address indexed to, uint256 value)',\n ]);\n if (txfer) {\n transfers.push({\n amount: txfer.decoded.value as bigint,\n blockNumber: tx.blockNumber as number,\n from: (txfer.decoded.from as string)?.toLowerCase() || NULL_ADDRESS,\n index: log.logIndex,\n timestamp,\n to: (txfer.decoded.to as string)?.toLowerCase() || NULL_ADDRESS,\n token: (log.address as string).toLowerCase(),\n tokenType: 'TOKEN',\n transactionGasFee,\n transactionHash: tx.hash,\n });\n }\n }\n }\n\n if (!TOKEN_TYPES.length || TOKEN_TYPES.includes('NFT')) {\n for (const log of tx.receipt.logs) {\n const txfer = evmDecodeLogWithMetadata(log, [\n 'Transfer(address indexed from, address indexed to, uint256 indexed value)',\n 'TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value)',\n 'TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values)',\n ]);\n\n if (!txfer) continue;\n\n switch (txfer.metadata.name) {\n case 'Transfer': {\n transfers.push({\n amount: 1,\n blockNumber: tx.blockNumber as number,\n from: (txfer.decoded.from as string)?.toLowerCase() || NULL_ADDRESS,\n index: log.logIndex,\n timestamp,\n to: (txfer.decoded.to as string)?.toLowerCase() || NULL_ADDRESS,\n token: (log.address as string).toLowerCase(),\n tokenId: (txfer.decoded.value as bigint).toString(),\n tokenType: 'NFT',\n transactionGasFee,\n transactionHash: tx.hash,\n });\n break;\n }\n case 'TransferSingle': {\n transfers.push({\n amount: txfer.decoded.value as bigint,\n blockNumber: tx.blockNumber as number,\n from: (txfer.decoded.from as string)?.toLowerCase() || NULL_ADDRESS,\n index: log.logIndex,\n timestamp,\n to: (txfer.decoded.to as string)?.toLowerCase() || NULL_ADDRESS,\n token: (log.address as string).toLowerCase(),\n tokenId: (txfer.decoded.id as bigint).toString(),\n tokenType: 'NFT',\n transactionGasFee,\n transactionHash: tx.hash,\n });\n break;\n }\n case 'TransferBatch': {\n for (let i = 0; i < (txfer.decoded.ids as bigint[]).length; i += 1) {\n transfers.push({\n amount: txfer.decoded.values[i] as bigint,\n blockNumber: tx.blockNumber as number,\n from: (txfer.decoded.from as string)?.toLowerCase() || NULL_ADDRESS,\n index: log.logIndex,\n timestamp,\n to: (txfer.decoded.to as string)?.toLowerCase() || NULL_ADDRESS,\n token: (log.address as string).toLowerCase(),\n tokenId: (txfer.decoded.ids[i] as bigint).toString(),\n tokenType: 'NFT',\n transactionGasFee,\n transactionHash: tx.hash,\n });\n }\n break;\n }\n }\n }\n }\n }\n if (typeof _ctx.params.contractAddress === 'string') {\n _ctx.params.contractAddress = _ctx.params.contractAddress.toLowerCase();\n }\n if (typeof _ctx.params.walletAddress === 'string') {\n _ctx.params.walletAddress = _ctx.params.walletAddress.toLowerCase();\n }\n\n return transfers;\n },\n\n tests: [\n {\n params: {\n network: 'BASE',\n walletAddress: '0x4F80864cD68782144e3736626896990acAe15a11',\n contractAddress: '0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed',\n },\n payload: 'https://jiti.indexing.co/networks/base/23507423',\n output: [\n {\n amount: 1000000000000000000000n,\n blockNumber: 23507423,\n from: '0x053002b4b332b422733c9469ddf9990bb6235e3d',\n index: 536,\n timestamp: '2024-12-10T04:16:33.000Z',\n to: '0x4f80864cd68782144e3736626896990acae15a11',\n token: '0x4ed4e862860bed51a9570b96d89af5e1b0efefed',\n tokenType: 'TOKEN',\n transactionGasFee: 1192354854229n,\n transactionHash: '0x69c9b12ccbe2d4f2f1dfc7c4a8557fc099fc5df276424417815acbc79a06fd56',\n },\n ],\n },\n ],\n};\n","import { SubTemplate } from '../../types';\nimport { NetworkTransfer } from './types';\n\nexport const FilecoinTokenTransfers: SubTemplate = {\n match: (block) => ['FILECOIN'].includes(block._network as string),\n\n transform(block) {\n let transfers: NetworkTransfer[] = [];\n\n const typedBlock = block as {\n Height: number;\n Blocks: Array<{ ParentBaseFee: string; Timestamp: number }>;\n messages: Array<{\n blockMessages: {\n BlsMessages?: Array<unknown>;\n SecpkMessages?: Array<{\n Message: {\n From: string;\n To: string;\n Value: string;\n GasFeeCap: string;\n GasPremium: string;\n };\n CID: { '/': string };\n }>;\n };\n }>;\n receipts: Array<{ GasUsed: number }>;\n };\n\n const blockNumber = typedBlock.Height;\n const blockTimestamp = new Date(typedBlock.Blocks[0].Timestamp * 1000).toISOString();\n const parentBaseFee = BigInt(typedBlock.Blocks[0].ParentBaseFee);\n\n let receiptIndex = 0;\n\n for (const msgGroup of typedBlock.messages) {\n const secpkMessages = msgGroup.blockMessages.SecpkMessages || [];\n\n for (const msg of secpkMessages) {\n const receipt = typedBlock.receipts[receiptIndex++];\n const gasUsed = receipt ? BigInt(receipt.GasUsed) : BigInt(0);\n const gasFeeCap = BigInt(msg.Message.GasFeeCap);\n const gasPremium = BigInt(msg.Message.GasPremium);\n const baseFeeBurn = gasUsed * parentBaseFee;\n const minerTip = gasUsed * (gasPremium < gasFeeCap - parentBaseFee ? gasPremium : gasFeeCap - parentBaseFee);\n const transactionGasFee = baseFeeBurn + minerTip;\n\n transfers.push({\n amount: BigInt(msg.Message.Value),\n blockNumber,\n from: msg.Message.From,\n to: msg.Message.To,\n token: null,\n tokenType: 'NATIVE',\n timestamp: blockTimestamp,\n transactionGasFee,\n transactionHash: msg.CID['/'],\n });\n }\n }\n\n return transfers;\n },\n\n tests: [\n {\n params: {\n network: 'FILECOIN',\n walletAddress: 'f1e3aa3z6gkaqxxwmbbna5gf2frggswwjaeavx7bq',\n },\n payload: 'https://jiti.indexing.co/networks/filecoin/4818438',\n output: [\n {\n amount: 7896300000000000000n,\n blockNumber: 4818438,\n from: 'f1e3aa3z6gkaqxxwmbbna5gf2frggswwjaeavx7bq',\n timestamp: '2025-03-24T23:39:00.000Z',\n to: 'f1bqdligg7ipuiizvmdn7ijobhbkwaieh6z6lah5y',\n token: null,\n tokenType: 'NATIVE',\n transactionGasFee: 1592498365133760n,\n transactionHash: 'bafy2bzacecxud3tayyq3caagjej5srufcx5fufjuqkz3ltgfty27wdsrmqeew',\n },\n ],\n },\n ],\n};\n","import { SubTemplate } from '../../types';\nimport { NetworkTransfer } from './types';\n\nexport const RippleTokenTransfers: SubTemplate = {\n match: (block) => ['RIPPLE'].includes(block._network as string),\n\n transform(block) {\n let transfers: NetworkTransfer[] = [];\n\n if (!Array.isArray(block.transactions)) {\n return [];\n }\n\n for (const rawTx of block.transactions) {\n const typedTx = rawTx as {\n Account?: string;\n Amount?: string | { currency: string; issuer: string; value: string };\n Destination?: string;\n Fee?: string;\n hash?: string;\n TransactionType?: string;\n date?: number;\n metaData?: {\n delivered_amount?: string | { currency: string; issuer: string; value: string };\n };\n };\n if (typedTx.TransactionType !== 'Payment') {\n continue;\n }\n const deliveredOrAmount = typedTx.metaData?.delivered_amount ?? typedTx.Amount ?? '0';\n let tokenSymbol = 'XRP';\n let tokenType: 'NATIVE' | 'TOKEN' | 'NFT' = 'NATIVE';\n\n let parsedAmount: bigint;\n\n if (typeof deliveredOrAmount === 'object') {\n tokenSymbol = deliveredOrAmount.currency?.toUpperCase() ?? 'UNKNOWN';\n tokenType = 'TOKEN';\n const floatVal = parseFloat(deliveredOrAmount.value);\n const smallestUnit = Math.round(floatVal * 1_000_000);\n parsedAmount = BigInt(smallestUnit);\n } else {\n parsedAmount = BigInt(String(deliveredOrAmount));\n }\n transfers.push({\n amount: parsedAmount,\n blockNumber: parseInt(block.ledger_index as string, 10),\n from: typedTx.Account ?? 'UNKNOWN',\n timestamp: block.close_time_iso ? (block.close_time_iso as string) : null,\n to: typedTx.Destination ?? 'UNKNOWN',\n token: tokenSymbol,\n tokenType: tokenType,\n transactionGasFee: BigInt(typedTx.Fee ?? '0'),\n transactionHash: typedTx.hash ?? '',\n });\n }\n\n return transfers;\n },\n\n tests: [\n {\n params: {\n network: 'RIPPLE',\n walletAddress: 'rUUgoiJmjTPEbxfZ4RsS9pVS9Kv813Wpui',\n contractAddress: '',\n },\n payload: 'https://jiti.indexing.co/networks/ripple/88104659',\n output: [\n {\n amount: 238n,\n blockNumber: 88104659,\n from: 'rMAGnTv4eMWktZnhKa5cHcDiY84ZiKUaQm',\n timestamp: '2024-05-19T22:18:52Z',\n to: 'rUUgoiJmjTPEbxfZ4RsS9pVS9Kv813Wpui',\n token: 'XRP',\n tokenType: 'NATIVE',\n transactionGasFee: 15n,\n transactionHash: '03564E6109261CDE73FCC5062C2A0A70F365CB1A0F9408C065B60EC3E94E4DBF',\n },\n ],\n },\n ],\n};\n","import { SubTemplate } from '../../types';\nimport { NetworkTransfer } from './types';\n\nexport const SUITokenTransfers: SubTemplate = {\n match: (block) => ['SUI'].includes(block._network as string),\n\n transform(block) {\n let transfers: NetworkTransfer[] = [];\n\n const blockNumber = parseInt(block.sequence as string, 10);\n const blockTimestamp = new Date(parseInt(block.timestamp as string, 10)).toISOString();\n\n const transactions = (block.transactions as any[]) || [];\n for (const tx of transactions) {\n const transactionHash = tx.digest as string;\n\n let transactionGasFee = BigInt(0);\n if (tx.effects?.gasUsed) {\n const gu = tx.effects.gasUsed;\n transactionGasFee =\n BigInt(gu.computationCost) +\n BigInt(gu.storageCost) -\n BigInt(gu.storageRebate) +\n BigInt(gu.nonRefundableStorageFee);\n if (transactionGasFee < 0n) {\n transactionGasFee = 0n;\n }\n }\n const balanceChanges = (tx.balanceChanges as any[]) || [];\n for (const bc of balanceChanges) {\n let rawAmt = BigInt(bc.amount as string);\n if (rawAmt === 0n) continue;\n\n let fromAddr: string | undefined;\n let toAddr: string | undefined;\n\n const rawOwner =\n bc.owner?.AddressOwner ||\n bc.owner?.ObjectOwner ||\n bc.owner?.Shared?.initial_shared_version ||\n 'UNKNOWN_OWNER';\n\n if (rawAmt < 0n) {\n fromAddr = String(rawOwner);\n toAddr = undefined;\n rawAmt = -rawAmt;\n } else {\n fromAddr = undefined;\n toAddr = String(rawOwner);\n }\n\n transfers.push({\n blockNumber,\n from: fromAddr,\n to: toAddr,\n amount: rawAmt,\n token: bc.coinType as string,\n tokenType: 'NATIVE',\n timestamp: blockTimestamp,\n transactionHash,\n transactionGasFee,\n });\n }\n }\n\n return transfers;\n },\n\n tests: [\n {\n params: {\n network: 'SUI',\n walletAddress: '0x39b9e5942df9a4686ebe727534077281d6adcee15a9bb74d3e052e56d78b2744',\n contractAddress: '',\n },\n payload: 'https://jiti.indexing.co/networks/sui/132734364',\n output: [\n {\n blockNumber: 132734364,\n from: '0x39b9e5942df9a4686ebe727534077281d6adcee15a9bb74d3e052e56d78b2744',\n to: undefined,\n amount: 5321172n,\n token: '0x2::sui::SUI',\n tokenType: 'NATIVE',\n timestamp: '2025-04-11T14:57:40.091Z',\n transactionHash: '4JWC8DX8eKYhwvRgzesGNWsb5t5RUyQVSNibKxLRaN22',\n transactionGasFee: 5408344n,\n },\n {\n blockNumber: 132734364,\n from: '0x39b9e5942df9a4686ebe727534077281d6adcee15a9bb74d3e052e56d78b2744',\n to: undefined,\n amount: 825772n,\n token: '0x2::sui::SUI',\n tokenType: 'NATIVE',\n timestamp: '2025-04-11T14:57:40.091Z',\n transactionHash: '5bXBDeoYqXTawkf2bCDriAE2M6Vu2EgYKEgJ1hW4qagZ',\n transactionGasFee: 901544n,\n },\n {\n blockNumber: 132734364,\n from: '0x39b9e5942df9a4686ebe727534077281d6adcee15a9bb74d3e052e56d78b2744',\n to: undefined,\n amount: 5321248n,\n token: '0x2::sui::SUI',\n tokenType: 'NATIVE',\n timestamp: '2025-04-11T14:57:40.091Z',\n transactionHash: '6KZYwvaAk76AL9p5kjkA27Tj5Jy47ipCxLKiCXtBbngo',\n transactionGasFee: 5408496n,\n },\n {\n blockNumber: 132734364,\n from: '0x39b9e5942df9a4686ebe727534077281d6adcee15a9bb74d3e052e56d78b2744',\n to: undefined,\n amount: 5321096n,\n token: '0x2::sui::SUI',\n tokenType: 'NATIVE',\n timestamp: '2025-04-11T14:57:40.091Z',\n transactionHash: 'HcJAHtSUypt8z2us2HqDm7PzB5HKS3ASY86pDKNAsgnE',\n transactionGasFee: 5408192n,\n },\n ],\n },\n ],\n};\n","import { SubTemplate } from '../../types';\nimport { NetworkTransfer } from './types';\n\nexport const SolanaTokenTransfers: SubTemplate = {\n match: (block) => ['SOLANA'].includes(block._network as string),\n\n transform(block) {\n let transfers: NetworkTransfer[] = [];\n\n for (const tx of block.transactions as unknown[]) {\n const solanaTx = tx as {\n meta: {\n fee: number;\n loadedAddresses: { readonly: string[]; writable: string[] };\n postTokenBalances: {\n accountIndex: number;\n mint: string;\n owner: string;\n uiTokenAmount: { amount: string };\n }[];\n preTokenBalances: {\n accountIndex: number;\n mint: string;\n owner: string;\n uiTokenAmount: { amount: string };\n }[];\n postBalances: number[];\n preBalances: number[];\n };\n transaction: {\n message: { accountKeys: (string | { pubkey: string })[]; instructions: unknown[] };\n signatures: string[];\n };\n };\n const txHash = solanaTx.transaction.signatures[0];\n const timestamp = block.blockTime ? new Date((block.blockTime as number) * 1000).toISOString() : null;\n const allAccounts = solanaTx.transaction.message.accountKeys\n .concat(solanaTx.meta.loadedAddresses.writable)\n .concat(solanaTx.meta.loadedAddresses.readonly);\n\n let txFee = BigInt(solanaTx.meta.fee);\n if (txFee < BigInt(10)) {\n txFee = txFee * BigInt(Math.pow(10, 9));\n }\n\n const transfersByKey: Record<string, NetworkTransfer> = {};\n for (const post of solanaTx.meta.postTokenBalances) {\n let matched = false;\n for (const pre of solanaTx.meta.preTokenBalances) {\n if (post.mint === pre.mint && post.owner === pre.owner) {\n let diff = BigInt(post.uiTokenAmount.amount) - BigInt(pre.uiTokenAmount.amount);\n if (diff === BigInt(0)) {\n matched = true;\n continue;\n }\n const isNegDiff = diff < 0;\n if (diff < 0) {\n diff = -diff;\n }\n\n const key = `${post.mint}-${diff.toString()}`;\n const txfer: NetworkTransfer = {\n amount: diff,\n blockNumber: block.blockHeight as number,\n from: pre.owner,\n timestamp,\n to: post.owner,\n transactionGasFee: txFee,\n transactionHash: txHash,\n token: post.mint,\n tokenType: 'TOKEN',\n };\n if (isNegDiff) {\n delete txfer.to;\n } else {\n delete txfer.from;\n }\n transfersByKey[key] = Object.assign(transfersByKey[key] || {}, txfer);\n matched = true;\n }\n }\n\n if (!matched) {\n let diff = BigInt(post.uiTokenAmount.amount);\n const isNegDiff = diff < 0;\n if (diff < 0) {\n diff = -diff;\n }\n const key = `${post.mint}-${diff.toString()}`;\n const txfer: NetworkTransfer = {\n amount: diff,\n blockNumber: block.blockHeight as number,\n from: null,\n timestamp,\n to: post.owner,\n token: post.mint,\n tokenType: 'TOKEN',\n transactionGasFee: txFee,\n transactionHash: txHash,\n };\n if (isNegDiff) {\n delete txfer.to;\n } else {\n delete txfer.from;\n }\n transfersByKey[key] = Object.assign(transfersByKey[key] || {}, txfer);\n }\n }\n\n for (let i = 1; i < solanaTx.meta.postBalances.length; i += 1) {\n const post = solanaTx.meta.postBalances[i];\n const pre = solanaTx.meta.preBalances[i];\n if (post !== undefined && pre !== undefined && post !== pre) {\n let diff = BigInt(post) - BigInt(pre);\n if (diff < 0) {\n diff = -diff;\n }\n const key = `null-${diff.toString()}`;\n\n const txfer: NetworkTransfer = {\n amount: diff,\n blockNumber: block.blockHeight as number,\n from:\n post > pre\n ? typeof allAccounts[0] === 'string'\n ? allAccounts[0]\n : (allAccounts[0] as { pubkey: string })?.pubkey\n : typeof allAccounts[i] === 'string'\n ? (allAccounts[i] as string)\n : (allAccounts[i] as { pubkey: string })?.pubkey,\n timestamp,\n to:\n typeof allAccounts[i] === 'string'\n ? (allAccounts[i] as string)\n : (allAccounts[i] as { pubkey: string })?.pubkey?.toString(),\n token: null,\n tokenType: 'NATIVE',\n transactionGasFee: txFee,\n transactionHash: txHash,\n };\n if (post > pre) {\n if (transfersByKey[key]) {\n delete txfer.from;\n }\n } else {\n delete txfer.to;\n }\n transfersByKey[key] = Object.assign(transfersByKey[key] || {}, txfer);\n }\n }\n\n const unmatchedFrom: Record<string, NetworkTransfer[]> = {};\n const unmatchedTo: Record<string, NetworkTransfer[]> = {};\n for (const key in transfersByKey) {\n const txfer = transfersByKey[key];\n if (!txfer.from) {\n if (!unmatchedFrom[txfer.token]) unmatchedFrom[txfer.token] = [];\n unmatchedFrom[txfer.token].push(txfer);\n delete transfersByKey[key];\n } else if (!txfer.to) {\n if (!unmatchedTo[txfer.token]) unmatchedTo[txfer.token] = [];\n unmatchedTo[txfer.token].push(txfer);\n delete transfersByKey[key];\n }\n }\n\n for (const token in unmatchedFrom) {\n if (\n unmatchedTo[token]?.length &&\n unmatchedTo[token].reduce((a, b) => a + BigInt(b.amount), BigInt(0)) -\n unmatchedFrom[token].reduce((a, b) => a + BigInt(b.amount), BigInt(0)) ===\n BigInt(0)\n ) {\n unmatchedTo[token].sort((a, b) => (a.amount > b.amount ? 1 : -1));\n unmatchedFrom[token].forEach((um) => {\n um.from = unmatchedTo[token][0].from;\n transfersByKey[`${token}-${um.amount.toString()}`] = um;\n });\n }\n }\n\n transfers.push(...Object.values(transfersByKey));\n }\n\n return transfers;\n },\n\n tests: [\n {\n params: {\n network: 'SOLANA',\n walletAddress: 'D89hHJT5Aqyx1trP6EnGY9jJUB3whgnq3aUvvCqedvzf',\n contractAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',\n },\n payload: 'https://jiti.indexing.co/networks/solana/326286476',\n output: [\n {\n amount: 3000350n,\n blockNumber: 304546603,\n from: 'D89hHJT5Aqyx1trP6EnGY9jJUB3whgnq3aUvvCqedvzf',\n timestamp: '2025-03-12T14:42:16.000Z',\n to: 'HTd5J9YhYnN1nwCAQiykpNBjoDrgtPVUcpk9TBPMCV4b',\n transactionGasFee: 105000n,\n transactionHash: '3HKaqRRPyA2NHvmf3xzJpgTemxXcextCRmgwWfHpKhDqDbJSqCdy8GtH5zG8tHQU2Dcznf7JgMP7sCLQSoNPw2E5',\n token: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',\n tokenType: 'TOKEN',\n },\n ],\n },\n {\n params: {\n network: 'SOLANA',\n walletAddress: '5u3gUxSsiqddQf1QhRDRQqDaFe9f5S3pyVkVWy77gB85',\n contractAddress: '',\n },\n payload: 'https://jiti.indexing.co/networks/solana/291562718',\n output: [\n {\n amount: 239999n,\n blockNumber: 270452291,\n from: '5u3gUxSsiqddQf1QhRDRQqDaFe9f5S3pyVkVWy77gB85',\n timestamp: '2024-09-23T16:45:56.000Z',\n to: 'CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM',\n token: null,\n tokenType: 'NATIVE',\n transactionGasFee: 45000n,\n transactionHash: '2TKq9VXjQocvyQGraxqRjyF8iASRPCmjJcGXkxYQbTknjFTToVB2rRSLMFCi5VwBx9uJ2fAN1YNt4Wr8TUVRuoMi',\n },\n {\n amount: 1n,\n blockNumber: 270452291,\n from: '5u3gUxSsiqddQf1QhRDRQqDaFe9f5S3pyVkVWy77gB85',\n timestamp: '2024-09-23T16:45:56.000Z',\n to: '8MCjBNEBEyT5uAnDBupUxQ8eiKHJcf4vh546g4uM7cjF',\n token: null,\n tokenType: 'NATIVE',\n transactionGasFee: 45000n,\n transactionHash: '2TKq9VXjQocvyQGraxqRjyF8iASRPCmjJcGXkxYQbTknjFTToVB2rRSLMFCi5VwBx9uJ2fAN1YNt4Wr8TUVRuoMi',\n },\n ],\n },\n {\n params: {\n network: 'SOLANA',\n walletAddress: '5cuy7pMhTPhVZN9xuhgSbykRb986siGJb6vnEtkuBrSU',\n contractAddress: '27G8MtK7VtTcCHkpASjSDdkWWYfoqT6ggEuKidVJidD4',\n },\n payload: 'https://jiti.indexing.co/networks/solana/325076237',\n output: [\n {\n amount: 14216129n,\n blockNumber: 303338729,\n from: '8KbrpeSRYXYjWSSdG7gE1tR7Go8MmKKxKaei1gGc4U7Q',\n timestamp: '2025-03-07T01:16:59.000Z',\n to: '5cuy7pMhTPhVZN9xuhgSbykRb986siGJb6vnEtkuBrSU',\n transactionGasFee: 353096n,\n transactionHash: '54qvCYcmUvPX6K3KuKG1nGRJWq6696LxwPNsx7DX5rcRsLbFntGEuWCjHTVc5wMcUZhKs1MuXeqpuDswrvNjETNQ',\n token: '27G8MtK7VtTcCHkpASjSDdkWWYfoqT6ggEuKidVJidD4',\n tokenType: 'TOKEN',\n },\n ],\n },\n {\n params: {\n network: 'SOLANA',\n walletAddress: 'DgC9bBDvJYeVyTqcp8nW5F5USNvxBiZ9NMoTUVy5UVPz',\n },\n payload: 'https://jiti.indexing.co/networks/solana/332450156',\n output: [\n {\n amount: 402062750n,\n blockNumber: 310691098,\n from: 'DgC9bBDvJYeVyTqcp8nW5F5USNvxBiZ9NMoTUVy5UVPz',\n timestamp: '2025-04-10T02:29:35.000Z',\n to: '5yY5BGRgwa5rxvYPpMV9EkDpwp6w1vNNXUNzUCtMoFfR',\n token: null,\n tokenType: 'NATIVE',\n transactionGasFee: 80001n,\n transactionHash: '32T7ANVqz1sHBoKhfk3omrRqwDCJFYMi6TfuAwyqHPCZPCihdWTU9t9i5D6tGwuytWRwRqnEXksMPMWbFbfBzVUk',\n },\n {\n amount: 97937250n,\n blockNumber: 310691098,\n from: 'DgC9bBDvJYeVyTqcp8nW5F5USNvxBiZ9NMoTUVy5UVPz',\n timestamp: '2025-04-10T02:29:35.000Z',\n to: '9zF2ZWTjnk6UkyWRxNtqy9UHims9u7LSaHGyhA5PwDSx',\n token: null,\n tokenType: 'NATIVE',\n transactionGasFee: 80001n,\n transactionHash: '32T7ANVqz1sHBoKhfk3omrRqwDCJFYMi6TfuAwyqHPCZPCihdWTU9t9i5D6tGwuytWRwRqnEXksMPMWbFbfBzVUk',\n },\n {\n amount: 19796403663n,\n blockNumber: 310691098,\n timestamp: '2025-04-10T02:29:35.000Z',\n to: 'DgC9bBDvJYeVyTqcp8nW5F5USNvxBiZ9NMoTUVy5UVPz',\n transactionGasFee: 80001n,\n transactionHash: '32T7ANVqz1sHBoKhfk3omrRqwDCJFYMi6TfuAwyqHPCZPCihdWTU9t9i5D6tGwuytWRwRqnEXksMPMWbFbfBzVUk',\n token: 'CniPCE4b3s8gSUPhUiyMjXnytrEqUrMfSsnbBjLCpump',\n tokenType: 'TOKEN',\n from: '4acL7mD2J6GYJy2g3iVTvfpmHCQSZ1rb8DBuupjcVzHJ',\n },\n ],\n },\n {\n params: {\n network: 'SOLANA',\n walletAddress: 'J1dHwpKBs8Jo4n7jWEJWwMGNH2DJQnApBFPnnYXg74v7',\n contractAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',\n },\n payload: 'https://jiti.indexing.co/networks/solana/332822080',\n output: [\n {\n amount: 2500000n,\n blockNumber: 311062104,\n from: 'J1dHwpKBs8Jo4n7jWEJWwMGNH2DJQnApBFPnnYXg74v7',\n timestamp: '2025-04-11T19:36:39.000Z',\n to: '3LoAYHuSd7Gh8d7RTFnhvYtiTiefdZ5ByamU42vkzd76',\n token: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',\n tokenType: 'TOKEN',\n transactionGasFee: 350362n,\n transactionHash: 'xKTWvnhSRErcHCMozRMEue4MriNr1Any6LiaQzXrR7imZ1MpZxRqbyv9LLg4JQoDq4oJZpDqPmzxLCtMCkgj2hn',\n },\n {\n amount: 1373625000n,\n blockNumber: 311062104,\n from: 'J1dHwpKBs8Jo4n7jWEJWwMGNH2DJQnApBFPnnYXg74v7',\n timestamp: '2025-04-11T19:36:39.000Z',\n to: '5guD4Uz462GT4Y4gEuqyGsHZ59JGxFN4a3rF6KWguMcJ',\n token: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',\n tokenType: 'TOKEN',\n transactionGasFee: 350362n,\n transactionHash: 'xKTWvnhSRErcHCMozRMEue4MriNr1Any6LiaQzXrR7imZ1MpZxRqbyv9LLg4JQoDq4oJZpDqPmzxLCtMCkgj2hn',\n },\n {\n amount: 1123875000n,\n blockNumber: 311062104,\n from: 'J1dHwpKBs8Jo4n7jWEJWwMGNH2DJQnApBFPnnYXg74v7',\n timestamp: '2025-04-11T19:36:39.000Z',\n to: 'DH4xmaWDnTzKXehVaPSNy9tMKJxnYL5Mo5U3oTHFtNYJ',\n token: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',\n tokenType: 'TOKEN',\n transactionGasFee: 350362n,\n transactionHash: 'xKTWvnhSRErcHCMozRMEue4MriNr1Any6LiaQzXrR7imZ1MpZxRqbyv9LLg4JQoDq4oJZpDqPmzxLCtMCkgj2hn',\n },\n ],\n },\n ],\n};\n","import { SubTemplate } from '../../types';\nimport { NetworkTransfer } from './types';\n\nexport const StarknetTokenTransfers: SubTemplate = {\n match: (block) => ['STARKNET'].includes(block._network as string),\n\n transform(block) {\n let transfers: NetworkTransfer[] = [];\n\n if (!Array.isArray(block.transactions)) {\n return [];\n }\n\n for (const tx of block.transactions) {\n const typedTx = tx as {\n transaction_hash: string;\n sender_address: string;\n receipt?: {\n actual_fee?: {\n amount?: string;\n unit?: string;\n };\n events?: {\n keys: string[];\n data: string[];\n }[];\n };\n timestamp?: number | string;\n data_availability?: Record<string, unknown>;\n };\n\n const timestamp = block.timestamp ? new Date((block.timestamp as number) * 1000).toISOString() : null;\n\n let transactionGasFee = BigInt(0);\n if (typedTx?.receipt?.actual_fee?.amount) {\n transactionGasFee = BigInt(typedTx.receipt.actual_fee.amount);\n }\n\n const transactionHash = typedTx.transaction_hash;\n\n if (!typedTx.receipt?.events) {\n continue;\n }\n\n for (const event of typedTx.receipt.events) {\n if (!event.keys.includes('0x99cd8bde557814842a3121e8ddfd433a539b8c9f14bf31ebf108d12e6196e9')) {\n continue;\n }\n if (event.data.length < 3) {\n continue;\n }\n\n const [from, to, amountHex] = event.data;\n const amount = BigInt(amountHex);\n\n transfers.push({\n amount,\n blockNumber: block.block_number as number,\n from,\n timestamp,\n to,\n token: null,\n tokenType: 'NATIVE',\n transactionGasFee,\n transactionHash,\n });\n }\n }\n\n return transfers;\n },\n\n tests: [\n {\n params: {\n network: 'STARKNET',\n walletAddress: '0x309e6b209031362268d62d646a067365e6f6d6eb7f571b5212cbdfd5f26fe54',\n contractAddress: '',\n },\n payload: 'https://jiti.indexing.co/networks/starknet/1149460',\n output: [\n {\n amount: 0x1c286f74458fc6n,\n blockNumber: 1149460,\n from: '0x309e6b209031362268d62d646a067365e6f6d6eb7f571b5212cbdfd5f26fe54',\n timestamp: '2025-02-13T17:36:52.000Z',\n to: '0x1176a1bd84444c89232ec27754698e5d2e7e1a7f1539f12027f28b23ec9f3d8',\n token: null,\n tokenType: 'NATIVE',\n transactionGasFee: 7925758505095110n,\n transactionHash: '0x707203dba31f442ae9a5477e6a8906f3676effa0f1d3bb19cbbc14e1ddfe21',\n },\n ],\n },\n ],\n};\n","import { SubTemplate } from '../../types';\nimport { NetworkTransfer } from './types';\n\nexport const StellarTokenTransfers: SubTemplate = {\n match: (block) => ['STELLAR'].includes(block._network as string),\n\n transform(block) {\n let transfers: NetworkTransfer[] = [];\n\n for (const tx of block.transactions as unknown[]) {\n const typedTx = tx as {\n hash: string;\n created_at: string;\n fee_charged: string;\n operations: {\n type: string;\n from: string;\n to: string;\n amount: string;\n asset_type: string;\n asset_issuer: string;\n }[];\n };\n for (const op of typedTx.operations) {\n if (op.type === 'payment') {\n transfers.push({\n amount: BigInt(op.amount.replace('.', '')),\n blockNumber: block.sequence as number,\n from: op.from,\n timestamp: typedTx.created_at,\n to: op.to,\n token: op.asset_type === 'native' ? null : op.asset_issuer,\n tokenType: op.asset_type === 'native' ? 'NATIVE' : 'TOKEN',\n transactionGasFee: BigInt(typedTx.fee_charged),\n transactionHash: typedTx.hash,\n });\n }\n }\n }\n\n return transfers;\n },\n\n tests: [\n {\n params: {\n network: 'STELLAR',\n walletAddress: 'GA5KLTNAWV27IOTBX5PKUOMVWFMLX4X7CPMQJ4QLR3G266MMVL7NMA4X',\n contractAddress: 'GC4Z2TDXU4GXVLHOS5P5SU6HKBCP7NKN4TJ5ZGTVRBW7MCBZTU7SNUSA',\n },\n payload: 'https://jiti.indexing.co/networks/stellar/51720546',\n output: [\n {\n amount: 150000n,\n blockNumber: 51720546,\n from: 'GA5KLTNAWV27IOTBX5PKUOMVWFMLX4X7CPMQJ4QLR3G266MMVL7NMA4X',\n timestamp: '2024-05-18T04:41:39Z',\n to: 'GC4Z2TDXU4GXVLHOS5P5SU6HKBCP7NKN4TJ5ZGTVRBW7MCBZTU7SNUSA',\n token: 'GC4Z2TDXU4GXVLHOS5P5SU6HKBCP7NKN4TJ5ZGTVRBW7MCBZTU7SNUSA',\n tokenType: 'TOKEN',\n transactionGasFee: 100n,\n transactionHash: '4fb2441210cbe87f5003abdfa86f03bafa54f789ed041feccbda0bd054297c4d',\n },\n ],\n },\n ],\n};\n","import { SubTemplate } from '../../types';\nimport { NetworkTransfer } from './types';\n\nexport const SubstrateTokenTransfers: SubTemplate = {\n match: (block) => ['ASTAR', 'ENJIN', 'KUSAMA', 'POLKADOT', 'BITTENSOR'].includes(block._network as string),\n\n transform(block) {\n let transfers: NetworkTransfer[] = [];\n\n const typedBlock = block as {\n blockNumber: number;\n header: { number: string };\n extrinsics: {\n method: string;\n signer: string;\n args: any[];\n hash: string;\n }[];\n };\n\n const blockNumber = typedBlock.blockNumber;\n\n const timestampExtrinsic = typedBlock.extrinsics.find((ex) => ex.method === 'timestamp.set');\n const blockTimestamp = timestampExtrinsic\n ? new Date(Number(timestampExtrinsic.args[0].toString().replace(/,/g, ''))).toISOString()\n : new Date().toISOString();\n\n for (const extrinsic of typedBlock.extrinsics) {\n if (['balances.transfer', 'balances.transferKeepAlive'].includes(extrinsic.method)) {\n const from = extrinsic.signer;\n const to = (extrinsic.args[0] as { Id?: string })?.Id || '';\n const amount = BigInt((extrinsic.args[1] as string).replace(/,/g, ''));\n\n transfers.push({\n amount,\n blockNumber,\n from,\n to,\n token: null,\n tokenType: 'NATIVE',\n timestamp: blockTimestamp,\n transactionGasFee: 0n,\n transactionHash: extrinsic.hash,\n });\n }\n }\n\n return transfers;\n },\n\n tests: [\n {\n params: {\n network: 'BITTENSOR',\n walletAddress: '5G6WmJ4mym9oSQzUF5tr7LvsNMvH5vWtutZM8vMvRv1Wwy6J',\n contractAddress: '',\n },\n payload: 'https://jiti.indexing.co/networks/bittensor/2652896',\n output: [\n {\n amount: 2154999850n,\n blockNumber: 2652896,\n from: '5G6WmJ4mym9oSQzUF5tr7LvsNMvH5vWtutZM8vMvRv1Wwy6J',\n to: '5CFwmfLfL1Z6vXU6hgGksh6irDFpcVXaTTNRoqDGMyHrDKK1',\n token: null,\n tokenType: 'NATIVE',\n timestamp: '2024-03-28T12:53:36.001Z',\n transactionGasFee: 0n,\n transactionHash: '0x9ea55a8f40b8d7704f27964155476ab9c744ec2ef2b1f431ad0e05a3a6f3c1ab',\n },\n ],\n },\n ],\n};\n","import { SubTemplate } from '../../types';\nimport { NetworkTransfer } from './types';\n\nexport const TONTokenTransfers: SubTemplate = {\n match: (block) => ['TON'].includes(block._network as string),\n\n transform(block) {\n let transfers: NetworkTransfer[] = [];\n\n const blockNumber = block.seqno as number;\n const blockTimestamp = new Date((block.shards?.[0]?.gen_utime as number) * 1000).toISOString();\n\n for (const shard of (block.shards as any[]) || []) {\n for (const tx of (shard.transactions as any[]) || []) {\n const transactionLT = tx.transaction_id.lt as string;\n const transactionHash = tx.transaction_id.hash as string;\n const transactionFee = BigInt((tx.fee as string) || '0');\n\n const inVal = BigInt((tx.in_msg?.value as string) || '0');\n if (inVal > 0n) {\n transfers.push({\n blockNumber,\n from: tx.in_msg?.source?.account_address as string,\n to: tx.address?.account_address as string,\n amount: inVal,\n token: 'TON',\n tokenType: 'NATIVE',\n timestamp: blockTimestamp,\n transactionHash,\n transactionGasFee: transactionFee,\n });\n }\n\n for (const outMsg of (tx.out_msgs as any[]) || []) {\n const outVal = BigInt((outMsg.value as string) || '0');\n if (outVal > 0n) {\n transfers.push({\n blockNumber,\n from: outMsg.source?.account_address as string,\n to: outMsg.destination?.account_address as string,\n amount: outVal,\n token: 'TON',\n tokenType: 'NATIVE',\n timestamp: blockTimestamp,\n transactionHash,\n transactionGasFee: transactionFee,\n });\n }\n }\n }\n }\n\n return transfers;\n },\n\n tests: [\n {\n params: {\n network: 'TON',\n walletAddress: 'EQAFukUyzmHjUvOYDOjNE-wbZFFl2FWas1rFJoh8IiTsWD40',\n contractAddress: '',\n },\n payload: 'https://jiti.indexing.co/networks/ton/44919328',\n output: [\n {\n blockNumber: 44919328,\n from: 'EQAFukUyzmHjUvOYDOjNE-wbZFFl2FWas1rFJoh8IiTsWD40',\n to: 'EQCFTFAHOU3vFt2NiZhRD5dwuS0k7GS59vIg3WfCKwfaQGW2',\n amount: 10000000n,\n token: 'TON',\n tokenType: 'NATIVE',\n timestamp: '2025-02-13T23:10:18.000Z',\n transactionHash: 'Vh5cWr2uvCsdhoouBQ+EiUcF54os9oqvh8A/62EroQc=',\n transactionGasFee: 2355233n,\n },\n ],\n },\n ],\n};\n","import { SubTemplate } from '../../types';\nimport { NetworkTransfer } from './types';\n\nexport const UTXOTokenTransfers: SubTemplate = {\n match: (block) => ['BITCOIN', 'BITCOIN_TESTNET', 'LITECOIN', 'DOGECOIN'].includes(block._network as string),\n\n transform(block) {\n let transfers: NetworkTransfer[] = [];\n\n for (const tx of block.tx as Record<string, unknown>[]) {\n const timestamp = tx.time ? new Date((tx.time as number) * 1000).toISOString() : null;\n const vin = tx.vin[0] as { prevout?: { scriptPubKey: { address: string } }; vout?: number };\n const vout = tx.vout as { value: number; scriptPubKey?: { address: string; addresses?: string[] } }[];\n\n const fromVout = Math.min(vin.vout || 1000, vout.length - 1);\n const fromAddress =\n vin.prevout?.scriptPubKey?.address ||\n vout[fromVout]?.scriptPubKey?.address ||\n vout[fromVout]?.scriptPubKey?.addresses?.[0];\n if (!fromAddress) {\n continue;\n }\n\n for (const v of vout) {\n transfers.push({\n amount: BigInt(Math.round(v.value * Math.pow(10, 8))),\n blockNumber: block.height as number,\n from: fromAddress,\n timestamp,\n to: v.scriptPubKey.address || v.scriptPubKey.addresses?.[0],\n transactionGasFee: BigInt(Math.round(((tx.fee as number) || 0) * Math.pow(10, 8))),\n transactionHash: tx.txid as string,\n token: null,\n tokenType: 'NATIVE',\n });\n }\n }\n\n return transfers;\n },\n\n tests: [\n {\n params: {\n network: 'DOGECOIN',\n walletAddress: 'DMqRVLrhbam3Kcfddpxd6EYvEBbpi3bEpP',\n contractAddress: '',\n },\n payload: 'https://jiti.indexing.co/networks/dogecoin/1000075',\n output: [\n {\n amount: 1008521000000n,\n blockNumber: 1000075,\n from: 'DMqRVLrhbam3Kcfddpxd6EYvEBbpi3bEpP',\n to: 'DMqRVLrhbam3Kcfddpxd6EYvEBbpi3bEpP',\n token: null,\n tokenType: 'NATIVE',\n transactionGasFee: 0n,\n transactionHash: '9873fe46ab29f61cefdec498b691af68e0ad29a7599c94f42d2d4e9a5d461dbe',\n timestamp: '2015-12-13T19:59:52.000Z',\n },\n ],\n },\n ],\n};\n","import { Template } from '../../types';\n\nimport { AptosTokenTransfers } from './aptos';\nimport { CardanoTokenTransfers } from './cardano';\nimport { CosmosTokenTransfers } from './cosmos';\nimport { EVMTokenTransfers } from './evm';\nimport { FilecoinTokenTransfers } from './filecoin';\nimport { NetworkTransfer } from './types';\nimport { RippleTokenTransfers } from './ripple';\nimport { SUITokenTransfers } from './sui';\nimport { SolanaTokenTransfers } from './solana';\nimport { StarknetTokenTransfers } from './starknet';\nimport { StellarTokenTransfers } from './stellar';\nimport { SubstrateTokenTransfers } from './substrate';\nimport { TONTokenTransfers } from './ton';\nimport { UTXOTokenTransfers } from './utxo';\n\nconst SUB_TEMPLATES = [\n AptosTokenTransfers,\n CardanoTokenTransfers,\n FilecoinTokenTransfers,\n RippleTokenTransfers,\n SolanaTokenTransfers,\n StarknetTokenTransfers,\n StellarTokenTransfers,\n SubstrateTokenTransfers,\n SUITokenTransfers,\n TONTokenTransfers,\n UTXOTokenTransfers,\n];\n\nconst UNIVERSAL_SUB_TEMPLATES = [CosmosTokenTransfers, EVMTokenTransfers];\n\nconst tokenTransfersTemplate: Template = {\n key: 'token_transfers',\n name: 'Token Transfers',\n description: 'Get all token transfers for a set of token types.',\n tags: ['EVM', 'ERC20', 'ERC721', 'NFT', 'TOKEN'],\n disabled: false,\n params: [\n { key: 'network', name: 'Network', type: 'NETWORK', optional: false },\n { key: 'contractAddress', name: 'Contract Address', type: 'ADDRESS', optional: true },\n { key: 'walletAddress', name: 'Wallet Address', type: 'ADDRESS', optional: true },\n {\n key: 'tokenTypes',\n name: 'Token Types',\n type: 'STRING',\n multiple: true,\n optional: true,\n values: ['NATIVE', 'TOKEN', 'NFT'],\n },\n ],\n\n transform: (block, _ctx) => {\n let transfers: NetworkTransfer[] = [];\n\n for (const sub of SUB_TEMPLATES.concat(UNIVERSAL_SUB_TEMPLATES)) {\n if (sub.match(block)) {\n transfers = sub.transform(block, _ctx) as NetworkTransfer[];\n break;\n }\n }\n\n const seenTransfers = new Set<string>();\n\n transfers = transfers.filter((txfer) => {\n if (txfer.amount <= BigInt(0)) {\n return false;\n }\n if (_ctx.params.contractAddress && _ctx.params.contractAddress !== txfer.token) {\n return false;\n }\n if (_ctx.params.walletAddress && ![txfer.from, txfer.to].includes(_ctx.params.walletAddress as string)) {\n return false;\n }\n\n const key = `${txfer.transactionHash}-${txfer.from}-${txfer.to}-${txfer.amount}-${txfer.token}`;\n\n if (seenTransfers.has(key)) {\n return false;\n }\n\n seenTransfers.add(key);\n\n return true;\n });\n\n return transfers;\n },\n\n tests: SUB_TEMPLATES.concat(UNIVERSAL_SUB_TEMPLATES)\n .map((v) => v.tests)\n .flat(),\n};\n\nexport default tokenTransfersTemplate;\n","import rawTemplate from './raw';\nimport tokenTransfersTemplate from './token-transfers';\n\nexport const templates = [rawTemplate, tokenTransfersTemplate];\n","import { templates as templateList } from './templates';\nimport * as utils from './utils';\nimport * as types from './types';\n\nconst templates = templateList.reduce((a, b) => ({ ...a, [b.key]: Object.assign({}, b) }), {});\n\nexport { utils, templates, types };\n\nexport function getAllTemplates() {\n return templateList.slice();\n}\n\nexport function getTemplateByKey(key: string) {\n return templateList.slice().find((template) => template.key === key);\n}\n"],"names":[],"version":3,"file":"types.d.ts.map"}
|