evmDecoder
Decode smart contract events as a pipe.
import { evmDecoder , commonAbis } from "@subsquid/pipes/evm" ;
async function main () {
const decoder = evmDecoder ({
range: { from: 20000000 , to: 20100000 },
contracts: [ "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" ],
events: {
transfer: commonAbis . erc20 . events . Transfer ,
},
});
await source . pipe ( decoder ). pipeTo ( target );
}
void main ();
Configuration
Range
Specify which blocks to decode:
// Fixed range
{ range : { from : 20000000 , to : 20100000 } }
// From latest
{ range : { from : 'latest' } }
// From specific block
{ range : { from : 20000000 } }
Contracts
Array of contract addresses:
{
contracts : [
"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" , // USDC
"0xdac17f958d2ee523a2206206994597c13d831ec7" , // USDT
"0x6b175474e89094c44da98b954eedeac495271d0f" , // DAI
];
}
Events
Map event names to ABI objects:
{
events : {
transfer : commonAbis . erc20 . events . Transfer ,
approval : commonAbis . erc20 . events . Approval
}
}
Error Handling
Custom error handler (optional, default: throws error):
{
onError : ( ctx , error ) => {
ctx . logger . error ({ error }, 'Decoding failed' );
// Return undefined to skip the failed event
// Or throw to stop processing
}
}
Common ABIs
Pre-defined ABIs for standard contracts:
import { commonAbis } from "@subsquid/pipes/evm" ;
// ERC20
commonAbis . erc20 . events . Transfer ;
commonAbis . erc20 . events . Approval ;
Currently, only ERC20 ABIs are available in commonAbis. For ERC721 and other token standards, use @subsquid/evm-typegen to generate custom ABIs from your contract JSON files.
Custom ABIs
Generate ABIs from contract JSON:
npx @subsquid/evm-typegen src/abi abi/uniswapV3Pool.json
Using @subsquid/evm-typegen provides full type safety for your event data and eliminates the need to manually look up raw event signatures (topic0 hashes). The generated types ensure compile-time safety when accessing event fields.
Use generated ABIs:
import * as uniswapAbi from "./abi/uniswapV3Pool" ;
const decoder = evmDecoder ({
range: { from: 12369621 },
contracts: [ "0x..." ],
events: {
swap: uniswapAbi . events . Swap ,
mint: uniswapAbi . events . Mint ,
burn: uniswapAbi . events . Burn ,
},
});
The property names in the events object (like swap, mint, burn) are arbitrary. You can use any names you prefer—these are just labels that help you organize and access the decoded events in your code.
Decoded Event Structure
{
blockNumber : 20000000 ,
timestamp : Date ,
contract : '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' ,
transactionHash : '0x...' ,
logIndex : 123 ,
rawEvent : {
address : '0x...' ,
data : '0x...' ,
topics : [ '0x...' ]
},
event : {
from : '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb' ,
to : '0x8e23Ee67d1332aD560396262C48ffbB273f626a4' ,
value : 1000000 n
}
}
Access decoded data:
async function main () {
for await ( const { data } of source . pipe ( decoder )) {
for ( const transfer of data . transfer ) {
console . log ( transfer . event . from );
console . log ( transfer . event . to );
console . log ( transfer . event . value );
console . log ( transfer . block . number );
console . log ( transfer . timestamp );
}
}
}
void main ();
Multiple Event Types
Decode multiple events from same contract:
async function main () {
const decoder = evmDecoder ({
range: { from: 20000000 },
contracts: [ "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" ],
events: {
transfer: commonAbis . erc20 . events . Transfer ,
approval: commonAbis . erc20 . events . Approval ,
},
});
for await ( const { data } of source . pipe ( decoder )) {
console . log ( `Transfers: ${ data . transfer . length } ` );
console . log ( `Approvals: ${ data . approval . length } ` );
}
}
void main ();
Composite Decoders
Decode from multiple contracts simultaneously:
async function main () {
const pipeline = source . pipeComposite ({
usdc: evmDecoder ({
range: { from: 20000000 },
contracts: [ "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" ],
events: { transfer: commonAbis . erc20 . events . Transfer },
}),
usdt: evmDecoder ({
range: { from: 20000000 },
contracts: [ "0xdac17f958d2ee523a2206206994597c13d831ec7" ],
events: { transfer: commonAbis . erc20 . events . Transfer },
}),
});
for await ( const { data } of pipeline ) {
console . log ( `USDC: ${ data . usdc . transfer . length } ` );
console . log ( `USDT: ${ data . usdt . transfer . length } ` );
}
}
void main ();
Profiler Integration
const decoder = evmDecoder ({
profiler: { id: "ERC20 decoding" },
range: { from: 20000000 },
contracts: [ "0x..." ],
events: { transfer: commonAbis . erc20 . events . Transfer },
});
Data Flow
Complete Example
import {
evmPortalSource ,
evmDecoder ,
commonAbis ,
} from "@subsquid/pipes/evm" ;
async function main () {
const source = evmPortalSource ({
portal: "https://portal.sqd.dev/datasets/ethereum-mainnet" ,
});
const decoder = evmDecoder ({
range: { from: 20000000 , to: 20000100 },
contracts: [ "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" ],
events: {
transfer: commonAbis . erc20 . events . Transfer ,
},
});
for await ( const { data } of source . pipe ( decoder )) {
for ( const transfer of data . transfer ) {
console . log ({
from: transfer . event . from ,
to: transfer . event . to ,
amount: transfer . event . value . toString (),
block: transfer . block . number ,
});
}
}
}
void main ();
See all 32 lines