Traces
Check out the Caveats page to avoid common EvmBatchProcessor
configuration issues.
addTrace(options)
Subscribe to call execution traces. This allows for tracking internal calls. The options
object has the following structure:
{
// data requests
callTo?: string[]
callSighash?: string[]
createFrom?: string[]
rewardAuthor?: string[]
suicideRefundAddress?: string[]
type?: string[]
range?: {from: number, to?: number}
// related data retrieval
transaction?: boolean
subtraces?: boolean
}
The data requests here are:
type
: get traces of types from this set. Allowed types are'create' | 'call' | 'suicide' | 'reward'
.callTo
: getcall
traces to the addresses in this set.callSighash
: getcall
traces with signature hashes in this set.createFrom
: getcreate
traces from the addresses in this set.rewardAuthor
: getreward
traces where block authors are in this set.suicideRefundAddress
: getsuicide
traces where refund addresses in this set.range
: get traces from transactions from this range of blocks.
Enabling the transaction
flag will cause the processor to retrieve transactions that the traces belong to. Enabling subtraces
will cause the processor to retrieve the downstream traces in addition to those that matched the data requests. These extra data items will be added to the appropriate iterables within the block data.
Note that traces can also be requested by the addTransaction()
method as related data.
Selection of the exact data to be retrieved for each trace item is done with the setFields()
method documented on the Field selection page. Be aware that field selectors for traces do not share their names with the fields of trace data items, unlike field selectors for other data item types. This is due to traces varying their structure depending on the value of the type
field.
Examples
Exploring internal calls of a given transaction
For a mint
call to Uniswap V3 Positions NFT.
import {EvmBatchProcessor} from '@subsquid/evm-processor'
import {TypeormDatabase} from '@subsquid/typeorm-store'
const TARGET_TRANSACTION = '0xf178718219151463aa773deaf7d9367b8408e35a624550af975e089ca6e015ca'
const TO_CONTRACT = '0xc36442b4a4522e871399cd717abdd847ab11fe88' // Uniswap v3 Positions NFT
const METHOD_SIGHASH = '0x88316456' // mint
const processor = new EvmBatchProcessor()
.setDataSource({
archive: lookupArchive('eth-mainnet'),
chain: 'https://rpc.ankr.com/eth'
})
.setFinalityConfirmation(75)
.setBlockRange({ from: 16962349, to: 16962349 })
.addTransaction({
to: [TO_CONTRACT],
sighash: [METHOD_SIGHASH],
traces: true
})
.setFields({ trace: { callTo: true } })
processor.run(new TypeormDatabase(), async ctx => {
let involvedContracts = new Set<string>()
let traceCount = 0
for (let block of ctx.blocks) {
for (let trc of block.traces) {
if (trc.type === 'call' && trc.transaction?.hash === TARGET_TRANSACTION) {
involvedContracts.add(trc.action.to)
traceCount += 1
}
}
}
console.log(`txn ${TARGET_TRANSACTION} had ${traceCount-1} internal transactions`)
console.log(`${involvedContracts.size} contracts were involved in txn ${TARGET_TRANSACTION}:`)
involvedContracts.forEach(c => { console.log(c) })
})
Grabbing addresses of all contracts ever created on Ethereum
Full code is available in this branch. WARNING: will contain addresses of some contracts that failed to deploy.
import {EvmBatchProcessor} from '@subsquid/evm-processor'
import {TypeormDatabase} from '@subsquid/typeorm-store'
import {CreatedContract} from './model'
import {lookupArchive} from '@subsquid/archive-registry'
const processor = new EvmBatchProcessor()
.setDataSource({
archive: lookupArchive('eth-mainnet'),
})
.setFields({
trace: {
createResultAddress: true,
},
})
.addTrace({
type: ['create'],
transaction: true,
})
processor.run(new TypeormDatabase({supportHotBlocks: false}), async (ctx) => {
const contracts: Map<string, CreatedContract> = new Map()
const addresses: Set<string> = new Set()
for (let c of ctx.blocks) {
for (let trc of c.traces) {
if (trc.type === 'create' &&
trc.result?.address != null &&
trc.transaction?.hash !== undefined) {
contracts.set(trc.result.address, new CreatedContract({id: trc.result.address}))
}
}
}
await ctx.store.upsert([...contracts.values()])
})
Currently there is no convenient way to check whether a trace had effect on the chain state, but this feature will be added in future releases.