Skip to content

Commit 35da737

Browse files
committed
feat(gateway-v4): expose output tokens
1 parent 7daa187 commit 35da737

File tree

3 files changed

+158
-14
lines changed

3 files changed

+158
-14
lines changed

sdk/src/gateway/client.ts

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,6 +1007,10 @@ export class GatewayApiClient {
10071007
);
10081008
const orders: OnrampOrderResponse[] = await response.json();
10091009
return orders.map((order) => {
1010+
const outputTokenAddress =
1011+
order.outputTokenAddress ?? order.tokensReceived?.[0]?.token_address ?? undefined;
1012+
const outputTokenAmount = order.outputTokenAmount ?? order.tokensReceived?.[0]?.amount ?? undefined;
1013+
10101014
function getFinal<L, R>(base?: L, output?: R): NonNullable<L | R> {
10111015
return order.status
10121016
? order.strategyAddress
@@ -1019,7 +1023,17 @@ export class GatewayApiClient {
10191023
: (base as NonNullable<typeof base>);
10201024
}
10211025
const getTokenAddress = (): string => {
1022-
return getFinal(order.baseTokenAddress, order.outputTokenAddress);
1026+
return getFinal(order.baseTokenAddress, outputTokenAddress);
1027+
};
1028+
const getTokenAmount = () => {
1029+
let amount = order.satoshis - order.fee;
1030+
const token = getToken();
1031+
1032+
if (token && !outputTokenAmount) {
1033+
amount *= Math.pow(10, token.decimals - 8);
1034+
}
1035+
1036+
return getFinal(amount, outputTokenAmount);
10231037
};
10241038
const getToken = (): Token | undefined => {
10251039
return ADDRESS_LOOKUP[chainId][getTokenAddress()];
@@ -1036,6 +1050,30 @@ export class GatewayApiClient {
10361050
? convertOrderDetailsRawToOrderDetails(order.orderDetails)
10371051
: undefined;
10381052

1053+
const getOutputTokens = () => {
1054+
const tokens = order.tokensReceived
1055+
? order.tokensReceived
1056+
: outputTokenAmount && outputTokenAddress
1057+
? [{ amount: outputTokenAmount, token_address: outputTokenAddress }]
1058+
: [];
1059+
1060+
return tokens.map(({ amount, token_address }) => ({
1061+
amount: amount,
1062+
token: ADDRESS_LOOKUP[chainId][token_address],
1063+
}));
1064+
};
1065+
1066+
const getTokens = () => {
1067+
const tokens = order.tokensReceived
1068+
? order.tokensReceived
1069+
: [{ amount: getTokenAmount(), token_address: getTokenAddress() }];
1070+
1071+
return tokens.map(({ amount, token_address }) => ({
1072+
amount: amount,
1073+
token: ADDRESS_LOOKUP[chainId][token_address],
1074+
}));
1075+
};
1076+
10391077
return {
10401078
...order,
10411079
orderDetails,
@@ -1044,14 +1082,9 @@ export class GatewayApiClient {
10441082
outputToken: ADDRESS_LOOKUP[chainId][order.outputTokenAddress!],
10451083
getTokenAddress,
10461084
getToken,
1047-
getTokenAmount() {
1048-
let amount = order.satoshis - order.fee;
1049-
const token = getToken();
1050-
if (token && !order.outputTokenAmount) {
1051-
amount *= Math.pow(10, token.decimals - 8);
1052-
}
1053-
return getFinal(amount, order.outputTokenAmount);
1054-
},
1085+
getTokenAmount,
1086+
getTokens,
1087+
getOutputTokens,
10551088
getConfirmations,
10561089
async getStatus(esploraClient: EsploraClient, latestHeight?: number): Promise<OrderStatus> {
10571090
const confirmations = await getConfirmations(esploraClient, latestHeight);

sdk/src/gateway/types.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,11 @@ export type OfframpGatewayCreateQuoteResponse = {
286286
gateway: Address;
287287
};
288288

289+
export interface TokenReceived {
290+
amount: string;
291+
token_address: Address;
292+
}
293+
289294
export interface OnrampOrderResponse {
290295
/** @description The gateway address */
291296
gatewayAddress: Address;
@@ -313,15 +318,17 @@ export interface OnrampOrderResponse {
313318
/** @description The amount of ETH received */
314319
outputEthAmount?: string;
315320
/** @description The output token (from strategies) */
316-
outputTokenAddress?: Address;
321+
outputTokenAddress?: Address | null;
317322
/** @description The output amount (from strategies) */
318-
outputTokenAmount?: string;
323+
outputTokenAmount?: string | null;
319324
/** @description The tx hash on the EVM chain */
320325
txHash?: string;
321326
/** @description V4 order details */
322327
orderDetails?: OrderDetailsRaw;
323328
/** layerzero dst eid if the order being routed through layerzero */
324329
layerzeroDstEid?: number;
330+
/** tokens received for gatway v4 order */
331+
tokensReceived: TokenReceived[] | null;
325332
}
326333

327334
export type OrderStatusData = {
@@ -349,12 +356,19 @@ export type OrderStatus =
349356
};
350357

351358
/** Order given by the Gateway API once the bitcoin tx is submitted */
352-
export type OnrampOrder = Omit<OnrampOrderResponse, 'satsToConvertToEth' | 'orderDetails'> & {
359+
export type OnrampOrder = Omit<
360+
OnrampOrderResponse,
361+
'satsToConvertToEth' | 'orderDetails' | 'tokensReceived' | 'outputTokenAddress' | 'outputTokenAmount'
362+
> & {
353363
/** @description The gas refill in satoshis */
354364
gasRefill: number;
355365
/** @description V4 order details */
356366
orderDetails?: OrderDetails;
357367
} & {
368+
/** @description The output token (from strategies) */
369+
outputTokenAddress?: Address;
370+
/** @description The output amount (from strategies) */
371+
outputTokenAmount?: string;
358372
/** @description Get the actual token address received */
359373
getTokenAddress(): string | undefined;
360374
/** @description Get the actual token received */
@@ -365,6 +379,10 @@ export type OnrampOrder = Omit<OnrampOrderResponse, 'satsToConvertToEth' | 'orde
365379
getConfirmations(esploraClient: EsploraClient, latestHeight?: number): Promise<number>;
366380
/** @description Get the actual order status */
367381
getStatus(esploraClient: EsploraClient, latestHeight?: number): Promise<OrderStatus>;
382+
/** @dcription Get all the output tokens */
383+
getOutputTokens(): Promise<{ amount: string; token: Token }[]>;
384+
/** @dcription Get all the tokens */
385+
getTokens(): Promise<{ amount: string | number; token: Token }[]>;
368386
} & GatewayTokensInfo;
369387

370388
export type GatewayTokensInfo = {

sdk/test/gateway.test.ts

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,8 +324,6 @@ describe('Gateway Tests', () => {
324324
const tokens = await gatewaySDK.getTokens(true);
325325
const enrichedTokens = await gatewaySDK.getEnrichedTokens(true);
326326

327-
assert.lengthOf(enrichedTokens, tokens.length);
328-
329327
enrichedTokens.forEach((enrichedToken, i) => {
330328
assert.strictEqual(tokens[i].address, enrichedToken.address);
331329
assert.strictEqual(tokens[i].name, enrichedToken.name);
@@ -436,6 +434,101 @@ describe('Gateway Tests', () => {
436434
assert.strictEqual(orders[7].layerzeroDstEid, 30111);
437435
});
438436

437+
it('should return multiple output tokens', async () => {
438+
const mockOrder = {
439+
gatewayAddress: zeroAddress,
440+
baseTokenAddress: TBTC_ADDRESS,
441+
txid: '',
442+
status: true,
443+
timestamp: 0,
444+
tokens: '0',
445+
satoshis: 0,
446+
fee: 0,
447+
txProofDifficultyFactor: 0,
448+
satsToConvertToEth: 0,
449+
strategyAddress: zeroAddress,
450+
};
451+
nock(`${MAINNET_GATEWAY_BASE_URL}`)
452+
.get(`/orders/${zeroAddress}`)
453+
.reply(200, [
454+
// pending
455+
{
456+
...mockOrder,
457+
status: false,
458+
strategyAddress: null,
459+
satoshis: 2000,
460+
outputTokenAddress: null,
461+
outputTokenAmount: null,
462+
tokensReceived: null,
463+
},
464+
// swapped - gateway v3
465+
{
466+
...mockOrder,
467+
satoshis: 2000,
468+
outputTokenAddress: TBTC_ADDRESS,
469+
outputTokenAmount: '2000',
470+
tokensReceived: null,
471+
},
472+
// swapped - gateway v4
473+
{
474+
...mockOrder,
475+
satoshis: 2000,
476+
outputTokenAddress: null,
477+
outputTokenAmount: null,
478+
tokensReceived: [{ amount: '2000', token_address: TBTC_ADDRESS }],
479+
},
480+
]);
481+
482+
const gatewaySDK = new GatewaySDK(bob.id);
483+
const orders = await gatewaySDK.getOnrampOrders(zeroAddress);
484+
assert.lengthOf(orders, 3);
485+
486+
assert.strictEqual(orders[0].getTokenAmount(), 20000000000000);
487+
assert.strictEqual(orders[1].getTokenAmount(), '2000');
488+
assert.strictEqual(orders[2].getTokenAmount(), '2000');
489+
490+
assert.strictEqual(orders[0].getTokenAddress(), TBTC_ADDRESS);
491+
assert.strictEqual(orders[1].getTokenAddress(), TBTC_ADDRESS);
492+
assert.strictEqual(orders[2].getTokenAddress(), TBTC_ADDRESS);
493+
494+
assert.strictEqual(orders[0].getToken()!.address, TBTC_ADDRESS);
495+
assert.strictEqual(orders[1].getToken()!.address, TBTC_ADDRESS);
496+
assert.strictEqual(orders[2].getToken()!.address, TBTC_ADDRESS);
497+
498+
assert.deepEqual(await orders[0].getOutputTokens(), []);
499+
assert.deepEqual(await orders[1].getOutputTokens(), [
500+
{
501+
amount: '2000',
502+
token: TBTC,
503+
},
504+
]);
505+
assert.deepEqual(await orders[2].getOutputTokens(), [
506+
{
507+
amount: '2000',
508+
token: TBTC,
509+
},
510+
]);
511+
512+
assert.deepEqual(await orders[0].getTokens(), [
513+
{
514+
amount: 20000000000000,
515+
token: TBTC,
516+
},
517+
]);
518+
assert.deepEqual(await orders[1].getTokens(), [
519+
{
520+
amount: '2000',
521+
token: TBTC,
522+
},
523+
]);
524+
assert.deepEqual(await orders[2].getTokens(), [
525+
{
526+
amount: '2000',
527+
token: TBTC,
528+
},
529+
]);
530+
});
531+
439532
it('should get valid create offramp quote', async () => {
440533
const gatewaySDK = new GatewaySDK(bobSepolia.id);
441534

0 commit comments

Comments
 (0)