From 2a93cba50af473bdd4fb5d23d2220dd8c9a1d681 Mon Sep 17 00:00:00 2001 From: Paul Cowgill Date: Fri, 15 Mar 2024 11:41:26 -0500 Subject: [PATCH] Frame referrer calldata attribution POC --- src/protocol/farcaster/frames.spec.ts | 26 ++ src/protocol/farcaster/frames.ts | 38 +++ .../farcasterFrame-0x9d360869.json | 299 ++++++++++++++++++ src/types/context.ts | 9 + 4 files changed, 372 insertions(+) create mode 100644 src/protocol/farcaster/frames.spec.ts create mode 100644 src/protocol/farcaster/frames.ts create mode 100644 src/test/transactions/farcasterFrame-0x9d360869.json diff --git a/src/protocol/farcaster/frames.spec.ts b/src/protocol/farcaster/frames.spec.ts new file mode 100644 index 00000000..65c1b6ee --- /dev/null +++ b/src/protocol/farcaster/frames.spec.ts @@ -0,0 +1,26 @@ +import { Transaction } from '../../types'; +import { detect, generate } from './frames'; +import farcasterFrame0x9d360869 from '../../test/transactions/farcasterFrame-0x9d360869.json'; +import catchall0xc35c01ac from '../../test/transactions/catchall-0xc35c01ac.json'; +import { containsBigInt, contextSummary } from '../../helpers/utils'; + +describe('Disperse', () => { + it('Should detect disperse transaction', () => { + const disperse1 = detect(farcasterFrame0x9d360869 as Transaction); + expect(disperse1).toBe(true); + }); + + it('Should generate context for disperseEth transaction', () => { + const disperse1 = generate(farcasterFrame0x9d360869 as Transaction); + expect(disperse1.context?.summaries?.category).toBe('PROTOCOL_1'); + expect(disperse1.context?.summaries?.en.title).toBe('Warpcast'); + const desc1 = contextSummary(disperse1.context); + expect(desc1).toBe('Warpcast'); + expect(containsBigInt(disperse1.context)).toBe(false); + }); + + it('Should not detect as disperseEth', () => { + const disperse1 = detect(catchall0xc35c01ac as Transaction); + expect(disperse1).toBe(false); + }); +}); diff --git a/src/protocol/farcaster/frames.ts b/src/protocol/farcaster/frames.ts new file mode 100644 index 00000000..daf90480 --- /dev/null +++ b/src/protocol/farcaster/frames.ts @@ -0,0 +1,38 @@ +import { Transaction } from '../../types'; + +export const contextualize = (transaction: Transaction): Transaction => { + const isEnjoy = detect(transaction); + if (!isEnjoy) return transaction; + + return generate(transaction); +}; + +// TODO: Consider modifying all other contextualizations to include this +// rather than making it a separate contextualization +export const detect = (transaction: Transaction): boolean => { + if (transaction.input.endsWith('fc000023c0')) { + return true; + } + + return false; +}; + +export const generate = (transaction: Transaction): Transaction => { + transaction.context = { + variables: { + referrer: { + type: 'referrer', + value: 'Warpcast', + rawValue: '0xfc000023c0', + }, + }, + summaries: { + category: 'PROTOCOL_1', + en: { + title: 'Warpcast', + default: '[[referrer]]', + }, + }, + }; + return transaction; +}; diff --git a/src/test/transactions/farcasterFrame-0x9d360869.json b/src/test/transactions/farcasterFrame-0x9d360869.json new file mode 100644 index 00000000..ca70605b --- /dev/null +++ b/src/test/transactions/farcasterFrame-0x9d360869.json @@ -0,0 +1,299 @@ +{ + "_id": "65f0e13d1c43d9c307855fbc", + "chainId": 10, + "hash": "0x9d36df09f11f93e2d9436f661e807e33e3e3354f11e3e2eb292f788b13430869", + "accessList": [], + "assetTransfers": [ + { + "from": "0x01b7baa7baa864fef3cd1c7bc118cc97cedcb33f", + "to": "0x00000000fcce7f938e7ae6d3c335bd6a1a7c593d", + "type": "eth", + "value": "786202250053159" + }, + { + "from": "0x00000000fcce7f938e7ae6d3c335bd6a1a7c593d", + "to": "0x01b7baa7baa864fef3cd1c7bc118cc97cedcb33f", + "type": "eth", + "value": "21965619766351" + } + ], + "attributes": [ + { + "key": "blockNumber", + "value": 117343167 + }, + { + "key": "chainId", + "value": 10 + }, + { + "key": "from", + "value": "0x01b7baa7baa864fef3cd1c7bc118cc97cedcb33f" + }, + { + "key": "parties", + "value": [ + "0x01b7baa7baa864fef3cd1c7bc118cc97cedcb33f", + "0x00000000fcce7f938e7ae6d3c335bd6a1a7c593d" + ] + }, + { + "key": "sigHash", + "value": "0x783a112b" + }, + { + "key": "timestamp", + "value": 1710285111 + }, + { + "key": "to", + "value": "0x00000000fcce7f938e7ae6d3c335bd6a1a7c593d" + }, + { + "key": "value", + "value": 0.000786202250053159 + }, + { + "key": "ethTransactors", + "value": [ + "0x01b7baa7baa864fef3cd1c7bc118cc97cedcb33f", + "0x00000000fcce7f938e7ae6d3c335bd6a1a7c593d" + ] + }, + { + "key": "eth", + "value": [ + 0.000764236630286808 + ] + } + ], + "baseFeePerGas": 2165, + "blockHash": "0xf4a4d4d90f93f00d994dc5f50a4961f35424edfa3dee907ebbb430557a19c542", + "blockNumber": 117343167, + "burntFees": "120016775", + "decode": { + "fragment": { + "type": "function", + "inputs": [ + { + "name": "", + "type": "uint256", + "baseType": "uint256", + "indexed": null, + "components": null, + "arrayLength": null, + "arrayChildren": null + }, + { + "name": "", + "type": "uint256", + "baseType": "uint256", + "indexed": null, + "components": null, + "arrayLength": null, + "arrayChildren": null + } + ], + "name": "rent", + "constant": false, + "outputs": [], + "stateMutability": "nonpayable", + "payable": false, + "gas": null + }, + "name": "rent", + "args": [ + "1024", + "1" + ], + "signature": "rent(uint256,uint256)", + "selector": "0x783a112b", + "value": "0" + }, + "from": "0x01b7baa7baa864fef3cd1c7bc118cc97cedcb33f", + "gas": 63631, + "gasPrice": "102165", + "input": "0x783a112b00000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000001fc000023c0", + "internalSigHashes": [ + { + "from": "0x01b7baa7baa864fef3cd1c7bc118cc97cedcb33f", + "to": "0x00000000fcce7f938e7ae6d3c335bd6a1a7c593d", + "sigHash": "0x783a112b" + }, + { + "from": "0x00000000fcce7f938e7ae6d3c335bd6a1a7c593d", + "to": "0x01b7baa7baa864fef3cd1c7bc118cc97cedcb33f", + "sigHash": "0x" + } + ], + "isoTimestamp": "2024-03-12T23:11:51.000Z", + "maxFeePerGas": "102518", + "maxPriorityFeePerGas": "100000", + "minerFees": "5543500000", + "netAssetTransfers": { + "0x01b7baa7baa864fef3cd1c7bc118cc97cedcb33f": { + "received": [], + "sent": [ + { + "asset": "eth", + "id": "eth", + "type": "eth", + "value": "764236630286808" + } + ] + }, + "0x00000000fcce7f938e7ae6d3c335bd6a1a7c593d": { + "received": [ + { + "asset": "eth", + "id": "eth", + "type": "eth", + "value": "764236630286808" + } + ], + "sent": [] + } + }, + "nonce": 27, + "parties": [ + "0x01b7baa7baa864fef3cd1c7bc118cc97cedcb33f", + "0x00000000fcce7f938e7ae6d3c335bd6a1a7c593d" + ], + "r": "0x1890d6aae01ff5a12e915e10ab19ab487ec9631618bbdb217334458cb1a3bc72", + "receipt": { + "blockHash": "0xf4a4d4d90f93f00d994dc5f50a4961f35424edfa3dee907ebbb430557a19c542", + "blockNumber": 117343167, + "contractAddress": null, + "cumulativeGasUsed": 212032, + "effectiveGasPrice": 102165, + "from": "0x01b7baa7baa864fef3cd1c7bc118cc97cedcb33f", + "gasUsed": 55435, + "l1Fee": "0x580f44ee128a", + "l1FeeScalar": "0.684", + "l1GasPrice": "0xdd90fcf16", + "l1GasUsed": "0x94c", + "logsBloom": "0x00000000010000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000200000000000000000000000000004000000000000000000000800000000800000000001000000000000000000000200000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000", + "status": true, + "to": "0x00000000fcce7f938e7ae6d3c335bd6a1a7c593d", + "transactionHash": "0x9d36df09f11f93e2d9436f661e807e33e3e3354f11e3e2eb292f788b13430869", + "transactionIndex": 5, + "type": "0x2" + }, + "s": "0x61938dc47396e2562d7d9a2ec8a916b123ea65dafc2f17c05394548995ce5ebf", + "sigHash": "0x783a112b", + "timestamp": 1710285111, + "to": "0x00000000fcce7f938e7ae6d3c335bd6a1a7c593d", + "transactionFee": "5663516775", + "transactionIndex": 5, + "type": 2, + "v": "0x1", + "value": "786202250053159", + "yParity": "0x1", + "logs": [ + { + "_id": "65f0e13dcaa84d6eb9514503", + "transactionHash": "0x9d36df09f11f93e2d9436f661e807e33e3e3354f11e3e2eb292f788b13430869", + "logIndex": 1, + "chainId": 10, + "address": "0x00000000fcce7f938e7ae6d3c335bd6a1a7c593d", + "blockHash": "0xf4a4d4d90f93f00d994dc5f50a4961f35424edfa3dee907ebbb430557a19c542", + "blockNumber": 117343167, + "data": "0x0000000000000000000000000000000000000000000000000000000000000001", + "decode": { + "fragment": { + "type": "event", + "inputs": [ + { + "name": "payer", + "type": "address", + "baseType": "address", + "indexed": true, + "components": null, + "arrayLength": null, + "arrayChildren": null + }, + { + "name": "fid", + "type": "uint256", + "baseType": "uint256", + "indexed": true, + "components": null, + "arrayLength": null, + "arrayChildren": null + }, + { + "name": "units", + "type": "uint256", + "baseType": "uint256", + "indexed": null, + "components": null, + "arrayLength": null, + "arrayChildren": null + } + ], + "name": "Rent", + "anonymous": false + }, + "name": "Rent", + "signature": "Rent(address,uint256,uint256)", + "topic": "0xaabd75b90fb7114eb9587a54f00ce5ebe8cb4a70627f3a6c26e506ffd771fe2f", + "args": [ + "0x01b7baa7baa864fef3cd1c7bc118cc97cedcb33f", + "1024", + "1" + ] + }, + "id": "log_c52b22bb", + "removed": false, + "topics": [ + "0xaabd75b90fb7114eb9587a54f00ce5ebe8cb4a70627f3a6c26e506ffd771fe2f", + "0x00000000000000000000000001b7baa7baa864fef3cd1c7bc118cc97cedcb33f", + "0x0000000000000000000000000000000000000000000000000000000000000400" + ], + "transactionIndex": 5 + } + ], + "contractsCreated": [], + "enrichedParties": { + "0x01b7baa7baa864fef3cd1c7bc118cc97cedcb33f": [ + { + "chainId": 1, + "isContract": false, + "ens": "gaby.eth", + "ensNew": { + "handle": "gaby.eth", + "avatar": "https://assets.airstack.xyz/image/nft/nNBFvZ6wvuIHqDzTFi5pM/pM0Q1IAUgJRNTJrw7f4s27kiONAZ6MbbNi4W2oO7aw05od/eUvfZhAXjitsAXBhwmt0k9keawHp0hhmWZYyFQimGXBivgdWt1QQICrgOCu84tM7i0gw1w+/hLkG0bTkglWWsmAOqPOLir3He1pVrA=/medium.svg" + }, + "farcaster": { + "handle": "gaby", + "avatar": "https://lh3.googleusercontent.com/7kZfysFMzyB_r1kJVXFXZ6S2skYNzPYlfFoVJTIt-7_dm5mJAy-xVsD1CijVUrAEBuXHDi8Ewu07qBRz8Le5NjShzvtCyHIr6tkc", + "fid": 83 + } + } + ], + "0x00000000fcce7f938e7ae6d3c335bd6a1a7c593d": [ + { + "chainId": 10, + "label": { + "public": "Farcaster: StorageRegistry" + }, + "isContract": true, + "tokenStandard": null, + "imgUrl": null, + "decimals": null, + "symbol": null, + "ensNew": { + "handle": null, + "avatar": null + }, + "farcaster": { + "handle": null, + "avatar": null, + "fid": null + }, + "ens": null + } + ] + }, + "assetsEnriched": {} +} \ No newline at end of file diff --git a/src/types/context.ts b/src/types/context.ts index 009725be..fe7dc1b9 100644 --- a/src/types/context.ts +++ b/src/types/context.ts @@ -76,6 +76,14 @@ export type ContextChainIDType = { emphasis?: boolean; }; +export type ContextReferrerType = { + type: 'referrer'; + value: string; + rawValue: string; + indexed?: boolean; + emphasis?: boolean; +}; + export type ContextNumberType = { type: 'number'; value: number; @@ -102,6 +110,7 @@ export type ContextSummaryVariableType = | ContextERC1155Type | ContextETHType | ContextChainIDType + | ContextReferrerType | ContextNumberType | ContextLinkType;