diff --git a/package-lock.json b/package-lock.json index a193a2b..b01e212 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "ajv": "^8.17.1", "axios": "^1.13.2", "jwt-decode": "^4.0.0", + "ox": "^0.13.1", "socket.io-client": "^4.8.1", "tsup": "^8.5.0", "viem": "^2.28.2" @@ -8673,9 +8674,9 @@ } }, "node_modules/ox": { - "version": "0.9.6", - "resolved": "https://registry.npmjs.org/ox/-/ox-0.9.6.tgz", - "integrity": "sha512-8SuCbHPvv2eZLYXrNmC0EC12rdzXQLdhnOMlHDW2wiCPLxBrOOJwX5L5E61by+UjTPOryqQiRSnjIKCI+GykKg==", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.13.1.tgz", + "integrity": "sha512-S6XU/Chz7HSSlAp7KYZigW8iwSyfS9M+GKTbyDQuZDsqC8S8GUFB2bLBLLB2SfHw8eE2MfymQazVXoLNljLycg==", "funding": [ { "type": "github", @@ -8690,7 +8691,7 @@ "@noble/hashes": "^1.8.0", "@scure/bip32": "^1.7.0", "@scure/bip39": "^1.6.0", - "abitype": "^1.0.9", + "abitype": "^1.2.3", "eventemitter3": "5.0.1" }, "peerDependencies": { @@ -8703,9 +8704,9 @@ } }, "node_modules/ox/node_modules/abitype": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.1.1.tgz", - "integrity": "sha512-Loe5/6tAgsBukY95eGaPSDmQHIjRZYQq8PB1MpsNccDIK8WiV+Uw6WzaIXipvaxTEL2yEB0OpEaQv3gs8pkS9Q==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.2.3.tgz", + "integrity": "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/wevm" @@ -10319,6 +10320,36 @@ } } }, + "node_modules/viem/node_modules/ox": { + "version": "0.9.6", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.9.6.tgz", + "integrity": "sha512-8SuCbHPvv2eZLYXrNmC0EC12rdzXQLdhnOMlHDW2wiCPLxBrOOJwX5L5E61by+UjTPOryqQiRSnjIKCI+GykKg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "^1.11.0", + "@noble/ciphers": "^1.3.0", + "@noble/curves": "1.9.1", + "@noble/hashes": "^1.8.0", + "@scure/bip32": "^1.7.0", + "@scure/bip39": "^1.6.0", + "abitype": "^1.0.9", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/viem/node_modules/ws": { "version": "8.18.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", diff --git a/package.json b/package.json index a8337d9..60762eb 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "ajv": "^8.17.1", "axios": "^1.13.2", "jwt-decode": "^4.0.0", + "ox": "^0.13.1", "socket.io-client": "^4.8.1", "tsup": "^8.5.0", "viem": "^2.28.2" diff --git a/src/contractClients/acpContractClientV2.ts b/src/contractClients/acpContractClientV2.ts index d3bcdd4..28c6e12 100644 --- a/src/contractClients/acpContractClientV2.ts +++ b/src/contractClients/acpContractClientV2.ts @@ -30,6 +30,8 @@ import { import { AcpX402 } from "../acpX402"; import { base, baseSepolia } from "viem/chains"; import MEMO_MANAGER_ABI from "../abis/memoManagerAbi"; +import { Attribution } from "ox/erc8021"; +import { appendBuilderCodeData } from "../utils"; class AcpContractClientV2 extends BaseAcpContractClient { private RETRY_CONFIG = { @@ -47,7 +49,8 @@ class AcpContractClientV2 extends BaseAcpContractClient { private memoManagerAddress: Address, private accountManagerAddress: Address, agentWalletAddress: Address, - config: AcpContractConfig = baseAcpConfigV2 + config: AcpContractConfig = baseAcpConfigV2, + private builderCode?: string ) { super(agentWalletAddress, config); } @@ -56,7 +59,8 @@ class AcpContractClientV2 extends BaseAcpContractClient { walletPrivateKey: Address, sessionEntityKeyId: number, agentWalletAddress: Address, - config: AcpContractConfig = baseAcpConfigV2 + config: AcpContractConfig = baseAcpConfigV2, + builderCode?: string ) { const publicClients: Record< number, @@ -106,7 +110,8 @@ class AcpContractClientV2 extends BaseAcpContractClient { memoManagerAddress.result as Address, accountManagerAddress.result as Address, agentWalletAddress, - config + config, + builderCode ); acpContractClient.publicClients = publicClients; @@ -221,10 +226,16 @@ class AcpContractClientV2 extends BaseAcpContractClient { throw new AcpError("Session key client not initialized"); } + const dataSuffix = this.builderCode + ? Attribution.toDataSuffix({ codes: [this.builderCode] }) + : undefined; + const basePayload: any = { uo: operations.map((operation) => ({ target: operation.contractAddress, - data: operation.data, + data: dataSuffix + ? appendBuilderCodeData(operation.data, dataSuffix as Hex) + : operation.data, value: operation.value, })), }; diff --git a/src/utils.ts b/src/utils.ts index 1fe3f91..bb89e9c 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,11 @@ -import { Address, decodeAbiParameters, encodeAbiParameters } from "viem"; +import { + Address, + concatHex, + decodeAbiParameters, + encodeAbiParameters, + Hex, + pad, +} from "viem"; import { arbitrum, arbitrumSepolia, @@ -118,3 +125,15 @@ export function encodeTransferEventMetadata( return result; } + +export function appendBuilderCodeData(data: Hex, suffix: Hex): Hex { + const opDataByteLength = (data.length - 2) / 2; + const suffixByteLength = (suffix.length - 2) / 2; + const opDataPaddedSize = Math.ceil(opDataByteLength / 32) * 32; + const suffixPaddedSize = Math.ceil(suffixByteLength / 32) * 32; + + const paddedData = pad(data, { size: opDataPaddedSize, dir: "right" }); + const paddedSuffix = pad(suffix, { size: suffixPaddedSize }); + + return concatHex([paddedData, paddedSuffix]); +}