From 3a7f0b9fe626f88d423322223f8ae91286182162 Mon Sep 17 00:00:00 2001 From: ponyjackal Date: Mon, 4 Mar 2024 08:49:35 -0800 Subject: [PATCH 1/2] feat: split cryptopunks from old nfts --- src/helpers/constants.ts | 8 +- .../assetTransfersCryptopunks.spec.ts | 71 +++++++++++++++++ .../ethereum/assetTransfersCryptopunks.ts | 78 +++++++++++++++++++ .../ethereum/assetTransfersOldNFTs.spec.ts | 63 +-------------- .../ethereum/assetTransfersOldNFTs.ts | 25 ------ src/transformers/index.ts | 2 + 6 files changed, 158 insertions(+), 89 deletions(-) create mode 100644 src/transformers/ethereum/assetTransfersCryptopunks.spec.ts create mode 100644 src/transformers/ethereum/assetTransfersCryptopunks.ts diff --git a/src/helpers/constants.ts b/src/helpers/constants.ts index 43f056b..086b625 100644 --- a/src/helpers/constants.ts +++ b/src/helpers/constants.ts @@ -403,8 +403,6 @@ export const EXECUTE_TRANSACTION_SIGNATURE = 'execTransaction(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,bytes)'; export const OLD_NFT_ADDRESSES = [ - '0x6ba6f2207e343923ba692e5cae646fb0f566db8d', // CRYPTOPUNKS_OLD - '0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb', // CRYPTOPUNKS_NEW '0x06012c8cf97bead5deae237070f9587f8e7a266d', // CryptoKitties '0xe897e5953ef250bd49875fe7a48254def92730b9', // FanBits '0x73b0ebea28f76be1368d578d13657354330472a9', // XART @@ -416,6 +414,12 @@ export const OLD_NFT_ADDRESSES = [ '0x323a3e1693e7a0959f65972f3bf2dfcb93239dfe', // Digital Art Chain '0x552d72f86f04098a4eaeda6d7b665ac12f846ad2', // Dark Winds ]; + +export const CRYPTO_PUNKS_ADDRESSES = [ + '0x6ba6f2207e343923ba692e5cae646fb0f566db8d', // CRYPTOPUNKS_OLD + '0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb', // CRYPTOPUNKS_NEW +]; + export const ERC721_TRANSFER_EVENT_1 = [ { anonymous: false, diff --git a/src/transformers/ethereum/assetTransfersCryptopunks.spec.ts b/src/transformers/ethereum/assetTransfersCryptopunks.spec.ts new file mode 100644 index 0000000..5caf0a2 --- /dev/null +++ b/src/transformers/ethereum/assetTransfersCryptopunks.spec.ts @@ -0,0 +1,71 @@ +import { transform as transactionAssetTransfers } from '../_common/assetTransfers'; +import { transform } from './assetTransfersCryptopunks'; +import { loadBlockFixture } from '../../helpers/utils'; + +describe('transactionAssetTransfersCryptopunks', () => { + it('should return transaction asset transfers CryptoPunks', () => { + // Special NFT transfers + /** CryptoPunks New */ + const cryptoPunksNewBlock = loadBlockFixture('ethereum', 5774644); + const cryptoPunksNewAssetResult = + transactionAssetTransfers(cryptoPunksNewBlock); + const cryptoPunksNewResult = transform(cryptoPunksNewAssetResult); + const cryptoPunksNewTx = cryptoPunksNewResult.transactions.find( + (tx) => + tx.hash === + '0x0da4c50900119b47400d71a9dd3563571145e4e362b952c36a9e38c77f7d25bb', + ); + expect(cryptoPunksNewTx).toBeDefined(); + if (cryptoPunksNewTx) { + const cryptoPunksNewTransfers = cryptoPunksNewTx.assetTransfers; + expect(cryptoPunksNewTransfers[0].type).toBe('erc721'); + if ('tokenId' in cryptoPunksNewTransfers[0]) { + expect(cryptoPunksNewTransfers[0].tokenId).toBe('89'); + } + } + /** CryptoPunks Old */ + const cryptoPunksOldBlock = loadBlockFixture('ethereum', 3862484); + const cryptoPunksOldAssetResult = + transactionAssetTransfers(cryptoPunksOldBlock); + const cryptoPunksOldResult = transform(cryptoPunksOldAssetResult); + const cryptoPunksOldTx = cryptoPunksOldResult.transactions.find( + (tx) => + tx.hash === + '0xff75a6739be926fe7328167011b5e2ac6a8883f55e76af70410520ef7b115901', + ); + expect(cryptoPunksOldTx).toBeDefined(); + if (cryptoPunksOldTx) { + const cryptoPunksOldTransfers = cryptoPunksOldTx.assetTransfers; + expect(cryptoPunksOldTransfers[0].type).toBe('erc721'); + if ('tokenId' in cryptoPunksOldTransfers[0]) { + expect(cryptoPunksOldTransfers[0].tokenId).toBe('4851'); + } + } + }); + + it('should return asset transfers for CryptoPunks transactions', () => { + const cryptoPunksBlock = loadBlockFixture('ethereum', '19321357_decoded'); + const cryptoPunksAssetResult = transactionAssetTransfers(cryptoPunksBlock); + const cryptoPunksResult = transform(cryptoPunksAssetResult); + const cryptoPunksTx = cryptoPunksResult.transactions.find( + (tx) => + tx.hash === + '0x4b581466cca3f2b50a6b97c053dd207feb911c6f858f21331ff829aa97dc6159', + ); + expect(cryptoPunksTx).toBeDefined(); + if (cryptoPunksTx) { + const cryptoPunksTransfers = cryptoPunksTx.assetTransfers; + expect(cryptoPunksTransfers.length).toBe(1); + if ('tokenId' in cryptoPunksTransfers[0]) { + expect(cryptoPunksTransfers[0].tokenId).toBe('7071'); + expect(cryptoPunksTransfers[0].from).toBe( + '0x4e6d2af4931681a024da8feaa4faba2bf8bbdc65', + ); + expect(cryptoPunksTransfers[0].to).toBe( + '0x1919db36ca2fa2e15f9000fd9cdc2edcf863e685', + ); + } + expect(cryptoPunksTransfers[0].type).toBe('erc721'); + } + }); +}); diff --git a/src/transformers/ethereum/assetTransfersCryptopunks.ts b/src/transformers/ethereum/assetTransfersCryptopunks.ts new file mode 100644 index 0000000..d0f90d4 --- /dev/null +++ b/src/transformers/ethereum/assetTransfersCryptopunks.ts @@ -0,0 +1,78 @@ +import { decodeEVMAddress } from '../../helpers/utils'; +import { + AssetType, + type AssetTransfer, + type RawBlock, + type RawTransaction, +} from '../../types'; +import { CRYPTO_PUNKS_ADDRESSES } from '../../helpers/constants'; + +const TRANSFER_SIGNATURES = { + // event PunkTransfer(address indexed from, address indexed to, uint256 punkIndex) + CRYPTO_PUNKS_ERC721: + '0x05af636b70da6819000c49f85b21fa82081c632069bb626f30932034099107d8', + // event PunkBought(uint indexed punkIndex, uint value, address indexed fromAddress, address indexed toAddress) + CRYPTO_PUNKS_ERC721_BUY: + '0x58e5d5a525e3b40bc15abaa38b5882678db1ee68befd2f60bafe3a7fd06db9e3', +}; + +function updateTokenTransfers(tx: RawTransaction) { + const oldNFTsTransfers: AssetTransfer[] = []; + + for (const log of tx.receipt.logs) { + if (!CRYPTO_PUNKS_ADDRESSES.includes(log.address)) { + continue; + } + + const [signature] = log.topics; + + switch (signature) { + case TRANSFER_SIGNATURES.CRYPTO_PUNKS_ERC721: + oldNFTsTransfers.push({ + asset: log.address, + from: decodeEVMAddress(log.topics[1]), + to: decodeEVMAddress(log.topics[2]), + tokenId: BigInt(log.data).toString(), + type: AssetType.ERC721, + }); + break; + case TRANSFER_SIGNATURES.CRYPTO_PUNKS_ERC721_BUY: + oldNFTsTransfers.push({ + asset: log.address, + from: decodeEVMAddress(log.topics[2]), + to: decodeEVMAddress(log.topics[3]), + tokenId: BigInt(log.topics[1]).toString(), + type: AssetType.ERC721, + }); + break; + default: + break; + } + } + + // filter old asset transfers from previous asset transfers + const nonOldAssetTransfers = tx.assetTransfers.filter( + (assetTransfer) => + assetTransfer.type !== AssetType.ETH && + !CRYPTO_PUNKS_ADDRESSES.includes(assetTransfer.asset), + ); + const assetTransfers = [...nonOldAssetTransfers, ...oldNFTsTransfers]; + + return assetTransfers; +} + +export function transform(block: RawBlock): RawBlock { + block.transactions = block.transactions.map((tx) => { + const hasOldNFTTransfer = tx.assetTransfers?.some( + (assetTransfer) => + assetTransfer.type !== AssetType.ETH && + CRYPTO_PUNKS_ADDRESSES.includes(assetTransfer.asset), + ); + if (hasOldNFTTransfer) { + tx.assetTransfers = updateTokenTransfers(tx); + } + return tx; + }); + + return block; +} diff --git a/src/transformers/ethereum/assetTransfersOldNFTs.spec.ts b/src/transformers/ethereum/assetTransfersOldNFTs.spec.ts index d34444a..824f46b 100644 --- a/src/transformers/ethereum/assetTransfersOldNFTs.spec.ts +++ b/src/transformers/ethereum/assetTransfersOldNFTs.spec.ts @@ -24,42 +24,7 @@ describe('transactionAssetTransfersOldNFTs', () => { } expect(cryptoKittiesTransfers[0].type).toBe('erc721'); } - /** CryptoPunks New */ - const cryptoPunksNewBlock = loadBlockFixture('ethereum', 5774644); - const cryptoPunksNewAssetResult = - transactionAssetTransfers(cryptoPunksNewBlock); - const cryptoPunksNewResult = transform(cryptoPunksNewAssetResult); - const cryptoPunksNewTx = cryptoPunksNewResult.transactions.find( - (tx) => - tx.hash === - '0x0da4c50900119b47400d71a9dd3563571145e4e362b952c36a9e38c77f7d25bb', - ); - expect(cryptoPunksNewTx).toBeDefined(); - if (cryptoPunksNewTx) { - const cryptoPunksNewTransfers = cryptoPunksNewTx.assetTransfers; - expect(cryptoPunksNewTransfers[0].type).toBe('erc721'); - if ('tokenId' in cryptoPunksNewTransfers[0]) { - expect(cryptoPunksNewTransfers[0].tokenId).toBe('89'); - } - } - /** CryptoPunks Old */ - const cryptoPunksOldBlock = loadBlockFixture('ethereum', 3862484); - const cryptoPunksOldAssetResult = - transactionAssetTransfers(cryptoPunksOldBlock); - const cryptoPunksOldResult = transform(cryptoPunksOldAssetResult); - const cryptoPunksOldTx = cryptoPunksOldResult.transactions.find( - (tx) => - tx.hash === - '0xff75a6739be926fe7328167011b5e2ac6a8883f55e76af70410520ef7b115901', - ); - expect(cryptoPunksOldTx).toBeDefined(); - if (cryptoPunksOldTx) { - const cryptoPunksOldTransfers = cryptoPunksOldTx.assetTransfers; - expect(cryptoPunksOldTransfers[0].type).toBe('erc721'); - if ('tokenId' in cryptoPunksOldTransfers[0]) { - expect(cryptoPunksOldTransfers[0].tokenId).toBe('4851'); - } - } + /** CryptoStriker */ const cryptoStrikersBlock = loadBlockFixture('ethereum', 15685187); const cryptoStrikersAssetResult = @@ -154,30 +119,4 @@ describe('transactionAssetTransfersOldNFTs', () => { expect(cryptoKittiesTransfers[0].type).toBe('erc721'); } }); - - it('should return asset transfers for CryptoPunks transactions', () => { - const cryptoPunksBlock = loadBlockFixture('ethereum', '19321357_decoded'); - const cryptoPunksAssetResult = transactionAssetTransfers(cryptoPunksBlock); - const cryptoPunksResult = transform(cryptoPunksAssetResult); - const cryptoPunksTx = cryptoPunksResult.transactions.find( - (tx) => - tx.hash === - '0x4b581466cca3f2b50a6b97c053dd207feb911c6f858f21331ff829aa97dc6159', - ); - expect(cryptoPunksTx).toBeDefined(); - if (cryptoPunksTx) { - const cryptoPunksTransfers = cryptoPunksTx.assetTransfers; - expect(cryptoPunksTransfers.length).toBe(1); - if ('tokenId' in cryptoPunksTransfers[0]) { - expect(cryptoPunksTransfers[0].tokenId).toBe('7071'); - expect(cryptoPunksTransfers[0].from).toBe( - '0x4e6d2af4931681a024da8feaa4faba2bf8bbdc65', - ); - expect(cryptoPunksTransfers[0].to).toBe( - '0x1919db36ca2fa2e15f9000fd9cdc2edcf863e685', - ); - } - expect(cryptoPunksTransfers[0].type).toBe('erc721'); - } - }); }); diff --git a/src/transformers/ethereum/assetTransfersOldNFTs.ts b/src/transformers/ethereum/assetTransfersOldNFTs.ts index b4ade60..1d632f3 100644 --- a/src/transformers/ethereum/assetTransfersOldNFTs.ts +++ b/src/transformers/ethereum/assetTransfersOldNFTs.ts @@ -17,13 +17,6 @@ import { decodeEventLog, Hex } from 'viem'; const TRANSFER_SIGNATURES = { // event Transfer(address,address,uint256) ERC721: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', - - // event PunkTransfer(address indexed from, address indexed to, uint256 punkIndex) - CRYPTO_PUNKS_ERC721: - '0x05af636b70da6819000c49f85b21fa82081c632069bb626f30932034099107d8', - // event PunkBought(uint indexed punkIndex, uint value, address indexed fromAddress, address indexed toAddress) - CRYPTO_PUNKS_ERC721_BUY: - '0x58e5d5a525e3b40bc15abaa38b5882678db1ee68befd2f60bafe3a7fd06db9e3', }; function updateTokenTransfers(tx: RawTransaction) { @@ -96,24 +89,6 @@ function updateTokenTransfers(tx: RawTransaction) { } } - break; - case TRANSFER_SIGNATURES.CRYPTO_PUNKS_ERC721: - oldNFTsTransfers.push({ - asset: log.address, - from: decodeEVMAddress(log.topics[1]), - to: decodeEVMAddress(log.topics[2]), - tokenId: BigInt(log.data).toString(), - type: AssetType.ERC721, - }); - break; - case TRANSFER_SIGNATURES.CRYPTO_PUNKS_ERC721_BUY: - oldNFTsTransfers.push({ - asset: log.address, - from: decodeEVMAddress(log.topics[2]), - to: decodeEVMAddress(log.topics[3]), - tokenId: BigInt(log.topics[1]).toString(), - type: AssetType.ERC721, - }); break; default: break; diff --git a/src/transformers/index.ts b/src/transformers/index.ts index 43e137b..d7f3801 100644 --- a/src/transformers/index.ts +++ b/src/transformers/index.ts @@ -9,12 +9,14 @@ import * as transactionParties from './_common/parties'; import * as transactionSigHash from './_common/sigHash'; import * as transactionTimestamp from './_common/timestamp'; import * as transactionAssetTransfersOldNFTs from './ethereum/assetTransfersOldNFTs'; +import * as transactionAssetTransfersCryptopunks from './ethereum/assetTransfersCryptopunks'; import * as transactionFees from './ethereum/fees'; import * as transactionForks from './ethereum/forks'; const children = { transactionAssetTransfers, transactionAssetTransfersOldNFTs, + transactionAssetTransfersCryptopunks, transactionDelegateCalls, transactionDerivativesNeighbors, transactionErrors, From 13b7bb515b3baf2f5435ec6eb7deae127d0b1b9e Mon Sep 17 00:00:00 2001 From: ponyjackal Date: Mon, 4 Mar 2024 08:53:50 -0800 Subject: [PATCH 2/2] feat: rename hasCryptopunksTransfer --- .../ethereum/assetTransfersCryptopunks.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/transformers/ethereum/assetTransfersCryptopunks.ts b/src/transformers/ethereum/assetTransfersCryptopunks.ts index d0f90d4..91031c4 100644 --- a/src/transformers/ethereum/assetTransfersCryptopunks.ts +++ b/src/transformers/ethereum/assetTransfersCryptopunks.ts @@ -17,7 +17,7 @@ const TRANSFER_SIGNATURES = { }; function updateTokenTransfers(tx: RawTransaction) { - const oldNFTsTransfers: AssetTransfer[] = []; + const cryptopunksTransfers: AssetTransfer[] = []; for (const log of tx.receipt.logs) { if (!CRYPTO_PUNKS_ADDRESSES.includes(log.address)) { @@ -28,7 +28,7 @@ function updateTokenTransfers(tx: RawTransaction) { switch (signature) { case TRANSFER_SIGNATURES.CRYPTO_PUNKS_ERC721: - oldNFTsTransfers.push({ + cryptopunksTransfers.push({ asset: log.address, from: decodeEVMAddress(log.topics[1]), to: decodeEVMAddress(log.topics[2]), @@ -37,7 +37,7 @@ function updateTokenTransfers(tx: RawTransaction) { }); break; case TRANSFER_SIGNATURES.CRYPTO_PUNKS_ERC721_BUY: - oldNFTsTransfers.push({ + cryptopunksTransfers.push({ asset: log.address, from: decodeEVMAddress(log.topics[2]), to: decodeEVMAddress(log.topics[3]), @@ -56,19 +56,19 @@ function updateTokenTransfers(tx: RawTransaction) { assetTransfer.type !== AssetType.ETH && !CRYPTO_PUNKS_ADDRESSES.includes(assetTransfer.asset), ); - const assetTransfers = [...nonOldAssetTransfers, ...oldNFTsTransfers]; + const assetTransfers = [...nonOldAssetTransfers, ...cryptopunksTransfers]; return assetTransfers; } export function transform(block: RawBlock): RawBlock { block.transactions = block.transactions.map((tx) => { - const hasOldNFTTransfer = tx.assetTransfers?.some( + const hasCryptopunksTransfer = tx.assetTransfers?.some( (assetTransfer) => assetTransfer.type !== AssetType.ETH && CRYPTO_PUNKS_ADDRESSES.includes(assetTransfer.asset), ); - if (hasOldNFTTransfer) { + if (hasCryptopunksTransfer) { tx.assetTransfers = updateTokenTransfers(tx); } return tx;