Skip to content

Commit 28160c4

Browse files
authored
fix: pass integratorId to swap API (#248)
1 parent 50c805d commit 28160c4

File tree

6 files changed

+100
-3
lines changed

6 files changed

+100
-3
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@across-protocol/app-sdk": patch
3+
---
4+
5+
Add integratorId to getSwapQuote action

packages/sdk/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@across-protocol/app-sdk",
3-
"version": "0.4.0",
3+
"version": "0.4.1",
44
"main": "./dist/index.js",
55
"type": "module",
66
"description": "The official SDK for integrating Across bridge into your dapp.",
@@ -63,4 +63,4 @@
6363
"peerDependencies": {
6464
"viem": "^2.31.2"
6565
}
66-
}
66+
}

packages/sdk/src/actions/getSwapQuote.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ export type GetSwapQuoteParams = Omit<
3333
slippage?: number;
3434
appFee?: number;
3535
actions?: Action[];
36+
/**
37+
* [Optional] Integrator identifier to be forwarded to the swap API so it can
38+
* append the integrator tag to the final deposit calldata when applicable.
39+
*/
40+
integratorId?: string;
3641
/**
3742
* [Optional] The logger to use.
3843
*/

packages/sdk/src/client.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,7 @@ export class AcrossClient {
661661
const quote = await getSwapQuote({
662662
...params,
663663
skipOriginTxEstimation: true,
664+
integratorId: params?.integratorId ?? this.integratorId,
664665
logger: params?.logger ?? this.logger,
665666
apiUrl: params?.apiUrl ?? this.apiUrl,
666667
});

packages/sdk/src/utils/hex.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Address, concat, Hex, isAddress, isHex, padHex } from "viem";
22

33
export const DOMAIN_CALLDATA_DELIMITER = "0x1dc0de";
4+
export const SWAP_CALLDATA_MARKER = "0x73c0de";
45

56
export function tagIntegratorId(integratorId: Hex, txData: Hex) {
67
assertValidIntegratorId(integratorId);
@@ -14,6 +15,25 @@ export function getIntegratorDataSuffix(integratorId: Hex) {
1415
return concat([DOMAIN_CALLDATA_DELIMITER, integratorId]);
1516
}
1617

18+
export function hasIntegratorIdAppended(
19+
calldata: Hex,
20+
integratorId: Hex,
21+
options: {
22+
isSwap?: boolean;
23+
} = {
24+
isSwap: false,
25+
},
26+
): boolean {
27+
const integratorIdSuffix = getIntegratorDataSuffix(integratorId);
28+
const swapSuffix = SWAP_CALLDATA_MARKER;
29+
// swap/approval first appends the integratorId, then the swap marker
30+
const suffix = options.isSwap
31+
? concat([integratorIdSuffix, swapSuffix])
32+
: integratorIdSuffix;
33+
34+
return calldata.endsWith(suffix.slice(2));
35+
}
36+
1737
export function isValidIntegratorId(integratorId: string) {
1838
return (
1939
isHex(integratorId) &&

packages/sdk/test/unit/actions/getSwapQuote.test.ts

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { assert, assertType, describe, test } from "vitest";
22
import { type SwapApprovalApiResponse } from "../../../src/api/swap-approval.js";
33
import { getSwapQuote } from "../../../src/actions/getSwapQuote.js";
4-
import { parseEther } from "viem";
4+
import { Hex, parseEther } from "viem";
5+
import { hasIntegratorIdAppended } from "../../../src/utils/hex.js";
6+
import { mainnetTestClient } from "../../common/sdk.js";
57

68
// Mainnet WETH
79
const inputToken = {
@@ -68,4 +70,68 @@ describe("getSwapQuote", () => {
6870
assert(quote, "No swap quote returned for the provided parameters");
6971
assertType<SwapApprovalApiResponse>(quote);
7072
});
73+
74+
test("swap approval calldata has integrator id appended", async () => {
75+
const integratorId: Hex = "0xdead";
76+
77+
const quote = await getSwapQuote({
78+
amount: parseEther(inputAmount),
79+
route: {
80+
originChainId: 1,
81+
inputToken: inputToken.address,
82+
destinationChainId: 10,
83+
outputToken: outputToken.address,
84+
},
85+
depositor: testRecipient,
86+
recipient: testRecipient,
87+
integratorId,
88+
});
89+
90+
assert(quote.swapTx, "swapTx missing in swap approval response");
91+
let data: Hex;
92+
if ("eip712" in quote.swapTx) {
93+
data = quote.swapTx.swapTx.data as Hex;
94+
} else {
95+
const simple = quote.swapTx as { data: string };
96+
data = simple.data as Hex;
97+
}
98+
99+
assert(
100+
hasIntegratorIdAppended(data, integratorId, {
101+
isSwap: true,
102+
}),
103+
"Expected swap calldata to have integrator id suffix",
104+
);
105+
});
106+
107+
test("client injects integratorId when omitted", async () => {
108+
const quote = await mainnetTestClient.getSwapQuote({
109+
amount: parseEther(inputAmount),
110+
route: {
111+
originChainId: 1,
112+
inputToken: inputToken.address,
113+
destinationChainId: 10,
114+
outputToken: outputToken.address,
115+
},
116+
depositor: testRecipient,
117+
recipient: testRecipient,
118+
// no integrator ID
119+
});
120+
121+
assert(quote.swapTx, "swapTx missing in swap approval response");
122+
let data: Hex;
123+
if ("eip712" in quote.swapTx) {
124+
data = quote.swapTx.swapTx.data as Hex;
125+
} else {
126+
const simple = quote.swapTx as { data: string };
127+
data = simple.data as Hex;
128+
}
129+
130+
// Client default integratorId is 0xdead when not configured explicitly
131+
const expectedIntegratorId: Hex = "0xdead";
132+
assert(
133+
hasIntegratorIdAppended(data, expectedIntegratorId, { isSwap: true }),
134+
"Expected swap calldata to include client's integrator id when omitted in params",
135+
);
136+
});
71137
});

0 commit comments

Comments
 (0)