From 89a333cd6f74769c50408abb251ffb2d7f8c4855 Mon Sep 17 00:00:00 2001 From: Gerhard Steenkamp Date: Wed, 24 Sep 2025 19:57:23 +0200 Subject: [PATCH 1/5] pass integratorId to swap api Signed-off-by: Gerhard Steenkamp --- packages/sdk/src/actions/getSwapQuote.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/sdk/src/actions/getSwapQuote.ts b/packages/sdk/src/actions/getSwapQuote.ts index 8ce18df..c794fdd 100644 --- a/packages/sdk/src/actions/getSwapQuote.ts +++ b/packages/sdk/src/actions/getSwapQuote.ts @@ -33,6 +33,11 @@ export type GetSwapQuoteParams = Omit< slippage?: number; appFee?: number; actions?: Action[]; + /** + * [Optional] Integrator identifier to be forwarded to the swap API so it can + * append the integrator tag to the final deposit calldata when applicable. + */ + integratorId?: string; /** * [Optional] The logger to use. */ From b276c1022945ee55d108dd184b6f8660275de24e Mon Sep 17 00:00:00 2001 From: Gerhard Steenkamp Date: Wed, 24 Sep 2025 19:57:35 +0200 Subject: [PATCH 2/5] propagate from client instance Signed-off-by: Gerhard Steenkamp --- packages/sdk/src/client.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/sdk/src/client.ts b/packages/sdk/src/client.ts index bc38852..12ce434 100644 --- a/packages/sdk/src/client.ts +++ b/packages/sdk/src/client.ts @@ -661,6 +661,7 @@ export class AcrossClient { const quote = await getSwapQuote({ ...params, skipOriginTxEstimation: true, + integratorId: params?.integratorId ?? this.integratorId, logger: params?.logger ?? this.logger, apiUrl: params?.apiUrl ?? this.apiUrl, }); From 10272f90e26292c767f8dea1c4dae07f3e24fe48 Mon Sep 17 00:00:00 2001 From: Gerhard Steenkamp Date: Wed, 24 Sep 2025 19:57:39 +0200 Subject: [PATCH 3/5] add test Signed-off-by: Gerhard Steenkamp --- packages/sdk/src/utils/hex.ts | 20 ++++++ .../test/unit/actions/getSwapQuote.test.ts | 68 ++++++++++++++++++- 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/packages/sdk/src/utils/hex.ts b/packages/sdk/src/utils/hex.ts index ecd0547..068c7c0 100644 --- a/packages/sdk/src/utils/hex.ts +++ b/packages/sdk/src/utils/hex.ts @@ -1,6 +1,7 @@ import { Address, concat, Hex, isAddress, isHex, padHex } from "viem"; export const DOMAIN_CALLDATA_DELIMITER = "0x1dc0de"; +export const SWAP_CALLDATA_MARKER = "0x73c0de"; export function tagIntegratorId(integratorId: Hex, txData: Hex) { assertValidIntegratorId(integratorId); @@ -14,6 +15,25 @@ export function getIntegratorDataSuffix(integratorId: Hex) { return concat([DOMAIN_CALLDATA_DELIMITER, integratorId]); } +export function hasIntegratorIdAppended( + calldata: Hex, + integratorId: Hex, + options: { + isSwap?: boolean; + } = { + isSwap: false, + }, +): boolean { + const integratorIdSuffix = getIntegratorDataSuffix(integratorId); + const swapSuffix = SWAP_CALLDATA_MARKER; + // swap/approval first appends the integratorId, then the swap marker + const suffix = options.isSwap + ? concat([integratorIdSuffix, swapSuffix]) + : integratorIdSuffix; + + return calldata.endsWith(suffix.slice(2)); +} + export function isValidIntegratorId(integratorId: string) { return ( isHex(integratorId) && diff --git a/packages/sdk/test/unit/actions/getSwapQuote.test.ts b/packages/sdk/test/unit/actions/getSwapQuote.test.ts index 6e330e8..892bf8a 100644 --- a/packages/sdk/test/unit/actions/getSwapQuote.test.ts +++ b/packages/sdk/test/unit/actions/getSwapQuote.test.ts @@ -1,7 +1,9 @@ import { assert, assertType, describe, test } from "vitest"; import { type SwapApprovalApiResponse } from "../../../src/api/swap-approval.js"; import { getSwapQuote } from "../../../src/actions/getSwapQuote.js"; -import { parseEther } from "viem"; +import { Hex, parseEther } from "viem"; +import { hasIntegratorIdAppended } from "../../../src/utils/hex.js"; +import { mainnetTestClient } from "../../common/sdk.js"; // Mainnet WETH const inputToken = { @@ -68,4 +70,68 @@ describe("getSwapQuote", () => { assert(quote, "No swap quote returned for the provided parameters"); assertType(quote); }); + + test("swap approval calldata has integrator id appended", async () => { + const integratorId: Hex = "0xdead"; + + const quote = await getSwapQuote({ + amount: parseEther(inputAmount), + route: { + originChainId: 1, + inputToken: inputToken.address, + destinationChainId: 10, + outputToken: outputToken.address, + }, + depositor: testRecipient, + recipient: testRecipient, + integratorId, + }); + + assert(quote.swapTx, "swapTx missing in swap approval response"); + let data: Hex; + if ("eip712" in quote.swapTx) { + data = quote.swapTx.swapTx.data as Hex; + } else { + const simple = quote.swapTx as { data: string }; + data = simple.data as Hex; + } + + assert( + hasIntegratorIdAppended(data, integratorId, { + isSwap: true, + }), + "Expected swap calldata to have integrator id suffix", + ); + }); + + test("client injects integratorId when omitted", async () => { + const quote = await mainnetTestClient.getSwapQuote({ + amount: parseEther(inputAmount), + route: { + originChainId: 1, + inputToken: inputToken.address, + destinationChainId: 10, + outputToken: outputToken.address, + }, + depositor: testRecipient, + recipient: testRecipient, + // no integrator ID + }); + + assert(quote.swapTx, "swapTx missing in swap approval response"); + let data: Hex; + if ("eip712" in quote.swapTx) { + data = quote.swapTx.swapTx.data as Hex; + } else { + const simple = quote.swapTx as { data: string }; + data = simple.data as Hex; + } + + // Client default integratorId is 0xdead when not configured explicitly + const expectedIntegratorId: Hex = "0xdead"; + assert( + hasIntegratorIdAppended(data, expectedIntegratorId, { isSwap: true }), + "Expected swap calldata to include client's integrator id when omitted in params", + ); + }); }); From 94bbefb3d1a2f437f0b75a75f22384be13ede16c Mon Sep 17 00:00:00 2001 From: Gerhard Steenkamp Date: Wed, 24 Sep 2025 20:06:47 +0200 Subject: [PATCH 4/5] bump sdk Signed-off-by: Gerhard Steenkamp --- packages/sdk/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 341bf86..fe69721 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@across-protocol/app-sdk", - "version": "0.4.0", + "version": "0.4.1", "main": "./dist/index.js", "type": "module", "description": "The official SDK for integrating Across bridge into your dapp.", @@ -63,4 +63,4 @@ "peerDependencies": { "viem": "^2.31.2" } -} \ No newline at end of file +} From 6a91bb8e6295bb452d6908576c80f3aee6f87920 Mon Sep 17 00:00:00 2001 From: Gerhard Steenkamp Date: Wed, 24 Sep 2025 20:07:59 +0200 Subject: [PATCH 5/5] add changeset Signed-off-by: Gerhard Steenkamp --- .changeset/selfish-turkeys-knock.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/selfish-turkeys-knock.md diff --git a/.changeset/selfish-turkeys-knock.md b/.changeset/selfish-turkeys-knock.md new file mode 100644 index 0000000..0b6ce18 --- /dev/null +++ b/.changeset/selfish-turkeys-knock.md @@ -0,0 +1,5 @@ +--- +"@across-protocol/app-sdk": patch +--- + +Add integratorId to getSwapQuote action