Skip to content

Commit

Permalink
feat: update common transformations
Browse files Browse the repository at this point in the history
  • Loading branch information
ponyjackal committed Jan 26, 2024
1 parent e90ce93 commit 5e2ea88
Show file tree
Hide file tree
Showing 24 changed files with 354 additions and 173 deletions.
5 changes: 2 additions & 3 deletions src/transformers/_common/contractABI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,8 @@ export function transform(block: RawBlock): TransactionContract[] {
isGnosisSafe: false,
whatsAbiSelectors: [],
isProxy: false,
whatsAbiAbi: {
type: 'constructor',
},
whatsAbiAbi: [],
tokenMetadata: { tokenStandard: '' },
};
}
// Get just the callable selectors
Expand Down
4 changes: 2 additions & 2 deletions src/transformers/_common/contractERC1155Detection.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Contract, RawBlock, TransactionContract } from '../types';
import { bytecodeIsERC1155 } from '../helpers/utils';
import type { Contract, RawBlock, TransactionContract } from '../../types';
import { bytecodeIsERC1155 } from '../../helpers/utils';

export function transform(block: RawBlock): TransactionContract[] {
const results: { hash: string; contracts: Contract[] }[] = [];
Expand Down
4 changes: 2 additions & 2 deletions src/transformers/_common/contractERC20Detection.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Contract, RawBlock, TransactionContract } from '../types';
import { bytecodeIsERC20 } from '../helpers/utils';
import type { Contract, RawBlock, TransactionContract } from '../../types';
import { bytecodeIsERC20 } from '../../helpers/utils';

export function transform(block: RawBlock): TransactionContract[] {
const results: { hash: string; contracts: Contract[] }[] = [];
Expand Down
4 changes: 2 additions & 2 deletions src/transformers/_common/contractERC721Detection.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Contract, RawBlock, TransactionContract } from '../types';
import { bytecodeIsERC721 } from '../helpers/utils';
import type { Contract, RawBlock, TransactionContract } from '../../types';
import { bytecodeIsERC721 } from '../../helpers/utils';

export function transform(block: RawBlock): TransactionContract[] {
const results: { hash: string; contracts: Contract[] }[] = [];
Expand Down
4 changes: 2 additions & 2 deletions src/transformers/_common/contractERC777Detection.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Contract, RawBlock, TransactionContract } from '../types';
import { bytecodeIsERC777 } from '../helpers/utils';
import type { Contract, RawBlock, TransactionContract } from '../../types';
import { bytecodeIsERC777 } from '../../helpers/utils';

export function transform(block: RawBlock): TransactionContract[] {
const results: { hash: string; contracts: Contract[] }[] = [];
Expand Down
22 changes: 12 additions & 10 deletions src/transformers/_common/contractGnosisSafeDetection.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import type { RawBlock, Contract, TransactionContract } from '../types';
import { bytecodeIsGnosisSafe } from '../helpers/utils';
import type { RawBlock, Contract, TransactionContract } from '../../types';
import { bytecodeIsGnosisSafe } from '../../helpers/utils';

export function transform(block: RawBlock): TransactionContract[] {
const result: TransactionContract[] = block.transactions
.filter((tx) => tx.contracts?.length > 0)
.filter((tx) => tx.contracts && tx.contracts.length > 0)
.map((tx) => {
const contracts: Contract[] = tx.contracts.map((txContract) => {
const contract: Contract = { ...txContract };
if (bytecodeIsGnosisSafe(txContract.bytecode)) {
contract.metadata.isGnosisSafe = true;
}
const contracts: Contract[] = tx.contracts
? tx.contracts.map((txContract) => {
const contract: Contract = { ...txContract };
if (bytecodeIsGnosisSafe(txContract.bytecode)) {
contract.metadata.isGnosisSafe = true;
}

return contract;
});
return contract;
})
: [];

return { hash: tx.hash, contracts };
});
Expand Down
22 changes: 12 additions & 10 deletions src/transformers/_common/contractGovernanceDetection.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import type { RawBlock, Contract, TransactionContract } from '../types';
import { bytecodeIsIGovernor } from '../helpers/utils';
import type { RawBlock, Contract, TransactionContract } from '../../types';
import { bytecodeIsIGovernor } from '../../helpers/utils';

export function transform(block: RawBlock): TransactionContract[] {
const result: TransactionContract[] = block.transactions
.filter((tx) => tx.contracts?.length > 0)
.filter((tx) => tx.contracts && tx.contracts.length > 0)
.map((tx) => {
const contracts: Contract[] = tx.contracts.map((txContract) => {
const contract: Contract = { ...txContract };
if (bytecodeIsIGovernor(txContract.bytecode)) {
contract.metadata.isGenericGovernance = true;
}
const contracts: Contract[] = tx.contracts
? tx.contracts.map((txContract) => {
const contract: Contract = { ...txContract };
if (bytecodeIsIGovernor(txContract.bytecode)) {
contract.metadata.isGenericGovernance = true;
}

return contract;
});
return contract;
})
: [];

return { hash: tx.hash, contracts };
});
Expand Down
28 changes: 18 additions & 10 deletions src/transformers/_common/contractSelfDestructed.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import { toBigNumber } from '../helpers/utils';
import type { RawBlock } from '../types';
import type { RawBlock, Contract } from '../../types';

type ContractSelfDestructed = { address: string; refundAddress: string; balance: string };
type ContractSelfDestructed = {
address: string;
refundAddress: string;
balance: string;
};

type ResultType = {
contractsCreated: Contract[];
contractSelfDestructed: ContractSelfDestructed[];
hash: string;
};

export function transform(block: RawBlock) {
const results: {
contractSelfDestructed: ContractSelfDestructed[];
hash: string;
}[] = [];
const results: ResultType[] = [];

for (const tx of block.transactions) {
const result = {
const result: ResultType = {
contractsCreated: [],
contractSelfDestructed: [],
hash: tx.hash,
Expand All @@ -22,8 +28,10 @@ export function transform(block: RawBlock) {
if (trace.type === 'suicide' && trace.action) {
result.contractSelfDestructed.push({
address: trace.action.address,
balance: toBigNumber(trace.action.balance).toString(),
refundAddress: trace.action.refundAddress,
balance: trace.action.balance
? BigInt(trace.action.balance).toString()
: '0',
refundAddress: trace.action.refundAddress ?? '',
});
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/transformers/_common/contractTimestamp.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import type { RawBlock, TransactionContract } from '../types';
import type { RawBlock, TransactionContract } from '../../types';

export function transform(block: RawBlock): TransactionContract[] {
const isoTimestamp = new Date(block.timestamp * 1000).toISOString();
const result: TransactionContract[] = [];

for (const tx of block.transactions) {
if (!tx.contracts) continue;

const contracts = tx.contracts.map((txContract) => {
txContract.timestamp = block.timestamp;
txContract.isoTimestamp = isoTimestamp;
Expand Down
90 changes: 51 additions & 39 deletions src/transformers/_common/transactionAssetTransfers.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import BigNumber from 'bignumber.js';
import Web3 from 'web3';

import { decodeEVMAddress, toBigNumber } from '../helpers/utils';
import type { AssetTransfer, RawBlock, RawTransaction } from '../types';
import { decodeEVMAddress } from '../../helpers/utils';
import {
AssetType,
type AssetTransfer,
type RawBlock,
type RawTransaction,
} from '../../types';
import { KNOWN_ADDRESSES } from '../../helpers/constants';
import { decodeAbiParameters, Hex } from 'viem';

// 1. pull out token transfers from logs
// 2. pull out ETH transfers from traces (this covers tx.value transfers)
Expand All @@ -17,22 +20,22 @@ const TRANSFER_SIGNATURES = {
ERC721: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',

// event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value)
ERC1155_SINGLE: '0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62',
ERC1155_SINGLE:
'0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62',

// event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values)
ERC1155_BATCH: '0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb',
ERC1155_BATCH:
'0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb',

// event Deposit(address indexed dst, uint wad)
WETH_DEPOSIT_ERC20: '0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c',
WETH_DEPOSIT_ERC20:
'0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c',

// event Withdrawal(address indexed src, uint wad)
WETH_WITHDRAW_ERC20: '0x7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65',
WETH_WITHDRAW_ERC20:
'0x7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65',
};

// @NOTE: we do *not* need access to an RPC here
// we just need an instance of Web3 in order to decode some log params
const web3 = new Web3();

function getTokenTransfers(tx: RawTransaction) {
const txAssetTransfers: AssetTransfer[] = [];

Expand All @@ -49,49 +52,49 @@ function getTokenTransfers(tx: RawTransaction) {
asset: log.address,
from: decodeEVMAddress(log.topics[1]),
to: decodeEVMAddress(log.topics[2]),
tokenId: toBigNumber(log.topics[3]).toString(),
type: 'erc721',
tokenId: BigInt(log.topics[3]).toString(),
type: AssetType.ERC721,
});
} else {
txAssetTransfers.push({
asset: log.address,
from: decodeEVMAddress(log.topics[1]),
to: decodeEVMAddress(log.topics[2]),
value: toBigNumber(log.data).toString(),
type: 'erc20',
value: BigInt(log.data).toString(),
type: AssetType.ERC20,
});
}
continue;
}

case TRANSFER_SIGNATURES.ERC1155_SINGLE: {
const { tokenId, value } = web3.eth.abi.decodeParameters(
const [tokenId, value] = decodeAbiParameters(
[
{ name: 'tokenId', type: 'uint256' },
{ name: 'value', type: 'uint256' },
],
log.data
) as { tokenId: BigNumber; value: BigNumber };
log.data as Hex,
);

txAssetTransfers.push({
asset: log.address,
from: decodeEVMAddress(log.topics[2]),
to: decodeEVMAddress(log.topics[3]),
tokenId: tokenId.toString(),
value: value.toString(),
type: 'erc1155',
type: AssetType.ERC1155,
});
continue;
}

case TRANSFER_SIGNATURES.ERC1155_BATCH: {
const { tokenIds, values } = web3.eth.abi.decodeParameters(
const [tokenIds, values] = decodeAbiParameters(
[
{ name: 'tokenIds', type: 'uint256[]' },
{ name: 'values', type: 'uint256[]' },
],
log.data
) as { tokenIds: BigNumber[]; values: BigNumber[] };
log.data as Hex,
);

for (let tokenIdx = 0; tokenIdx < tokenIds.length; tokenIdx += 1) {
txAssetTransfers.push({
Expand All @@ -100,7 +103,7 @@ function getTokenTransfers(tx: RawTransaction) {
to: decodeEVMAddress(log.topics[3]),
tokenId: tokenIds[tokenIdx].toString(),
value: values[tokenIdx].toString(),
type: 'erc1155',
type: AssetType.ERC1155,
});
}
continue;
Expand All @@ -115,8 +118,8 @@ function getTokenTransfers(tx: RawTransaction) {
asset: log.address,
from: KNOWN_ADDRESSES.NULL,
to: decodeEVMAddress(log.topics[1]),
value: toBigNumber(log.data).toString(),
type: 'erc20',
value: BigInt(log.data).toString(),
type: AssetType.ERC20,
});
continue;
}
Expand All @@ -130,8 +133,8 @@ function getTokenTransfers(tx: RawTransaction) {
asset: log.address,
from: decodeEVMAddress(log.topics[1]),
to: KNOWN_ADDRESSES.NULL,
value: toBigNumber(log.data).toString(),
type: 'erc20',
value: BigInt(log.data).toString(),
type: AssetType.ERC20,
});
continue;
}
Expand Down Expand Up @@ -159,10 +162,12 @@ export function transform(block: RawBlock) {
// then group by contract
const tokenTransfersByContract: Record<string, AssetTransfer[]> = {};
for (const transfer of tokenTransfers) {
if (!tokenTransfersByContract[transfer.asset]) {
tokenTransfersByContract[transfer.asset] = [];
if (transfer.type !== AssetType.ETH) {
if (!tokenTransfersByContract[transfer.asset]) {
tokenTransfersByContract[transfer.asset] = [];
}
tokenTransfersByContract[transfer.asset].push(transfer);
}
tokenTransfersByContract[transfer.asset].push(transfer);
}

// now prepare a final set of *all* asset transfers (including ETH)
Expand All @@ -173,21 +178,28 @@ export function transform(block: RawBlock) {
// check for ETH transfers
if (trace.action.callType !== 'delegatecall') {
// track contract suicides
if (trace.type === 'suicide' && trace.action.balance && trace.action.balance !== '0x0') {
if (
trace.type === 'suicide' &&
trace.action.balance &&
trace.action.balance !== '0x0'
) {
assetTransfers.push({
from: trace.action.address,
to: trace.action.refundAddress,
type: 'eth',
value: toBigNumber(trace.action.balance).toString(),
to: trace.action.refundAddress ?? '',
type: AssetType.ETH,
value: BigInt(trace.action.balance).toString(),
});
}
// otherwise track ETH transfers
else if (trace.action.value && trace.action.value !== '0x0') {
assetTransfers.push({
from: trace.action.from,
to: trace.type === 'create' ? trace.result.address : trace.action.to,
type: 'eth',
value: toBigNumber(trace.action.value).toString(),
to:
trace.type === 'create'
? trace.result.address ?? ''
: trace.action.to ?? '',
type: AssetType.ETH,
value: BigInt(trace.action.value).toString(),
});
}
}
Expand Down
Loading

0 comments on commit 5e2ea88

Please sign in to comment.