diff --git a/sdk/package-lock.json b/sdk/package-lock.json index c28d2cc9..02821532 100644 --- a/sdk/package-lock.json +++ b/sdk/package-lock.json @@ -1,12 +1,12 @@ { "name": "@gobob/bob-sdk", - "version": "2.3.1", + "version": "2.3.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@gobob/bob-sdk", - "version": "2.3.1", + "version": "2.3.2", "dependencies": { "@scure/base": "^1.1.7", "@scure/btc-signer": "^1.3.2", diff --git a/sdk/package.json b/sdk/package.json index ee8c8f6a..71500a0c 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@gobob/bob-sdk", - "version": "2.3.1", + "version": "2.3.2", "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { diff --git a/sdk/src/esplora.ts b/sdk/src/esplora.ts index 936b31fa..7c715451 100644 --- a/sdk/src/esplora.ts +++ b/sdk/src/esplora.ts @@ -69,12 +69,17 @@ export interface Transaction { scriptpubkey_address?: string value: number }> - status: { - confirmed: boolean - block_height?: number - block_hash?: string - block_time?: number - } + status: TransactionStatus +} + +/** + * @ignore + */ +export interface TransactionStatus { + confirmed: boolean + block_height?: number + block_hash?: string + block_time?: number } /** @@ -259,6 +264,10 @@ export class EsploraClient { return this.getJson(`${this.basePath}/tx/${txId}`); } + async getTransactionStatus(txId: string): Promise { + return this.getJson(`${this.basePath}/tx/${txId}/status`); + } + /** * Get the transaction data, represented as a hex string, for a Bitcoin transaction with a given ID (txId). * diff --git a/sdk/src/gateway/client.ts b/sdk/src/gateway/client.ts index babfcae3..0072bc0a 100644 --- a/sdk/src/gateway/client.ts +++ b/sdk/src/gateway/client.ts @@ -259,11 +259,41 @@ export class GatewayApiClient { const response = await this.fetchGet(`${this.baseUrl}/orders/${userAddress}`); const orders: GatewayOrderResponse[] = await response.json(); return orders.map((order) => { + function getFinal(base?: L, output?: R) { + return order.status + ? order.strategyAddress + ? output + ? output // success + : base // failed + : base // success + : order.strategyAddress // pending + ? output + : base; + } + const getTokenAddress = (): string | undefined => { + return getFinal(order.baseTokenAddress, order.outputTokenAddress); + } return { gasRefill: order.satsToConvertToEth, ...order, baseToken: ADDRESS_LOOKUP[order.baseTokenAddress], - outputToken: ADDRESS_LOOKUP[order.outputTokenAddress] + outputToken: ADDRESS_LOOKUP[order.outputTokenAddress], + getTokenAddress, + getToken() { + return ADDRESS_LOOKUP[getTokenAddress()]; + }, + getAmount(): string | number | undefined { + const baseAmount = order.satoshis - order.fee; + return getFinal(baseAmount, order.outputTokenAmount); + }, + async getConfirmations(esploraClient: EsploraClient, latestHeight?: number): Promise { + const txStatus = await esploraClient.getTransactionStatus(order.txid); + if (!latestHeight) { + latestHeight = await esploraClient.getLatestHeight(); + } + + return txStatus.confirmed ? latestHeight - txStatus.block_height! + 1 : 0; + } }; }); } diff --git a/sdk/src/gateway/types.ts b/sdk/src/gateway/types.ts index 2f806f18..8466d4c5 100644 --- a/sdk/src/gateway/types.ts +++ b/sdk/src/gateway/types.ts @@ -1,3 +1,5 @@ +import type { EsploraClient } from "../esplora"; + type ChainSlug = string | number; type TokenSymbol = string; @@ -187,7 +189,7 @@ export type GatewayCreateOrderRequest = { satoshis: number; }; -export type GatewayOrderResponse = { +export interface GatewayOrderResponse { /** @description The gateway address */ gatewayAddress: EvmAddress; /** @description The base token address (e.g. wBTC or tBTC) */ @@ -216,6 +218,14 @@ export type GatewayOrderResponse = { outputTokenAddress?: EvmAddress; /** @description The output amount (from strategies) */ outputTokenAmount?: string; + /** @description Get the actual token address received */ + getTokenAddress(): string | undefined; + /** @description Get the actual token received */ + getToken(): Token | undefined; + /** @description Get the actual amount received of the token */ + getAmount(): string | number | undefined; + /** @description Get the number of confirmations */ + getConfirmations(esploraClient: EsploraClient, latestHeight?: number): Promise; }; /** Order given by the Gateway API once the bitcoin tx is submitted */ diff --git a/sdk/test/gateway.test.ts b/sdk/test/gateway.test.ts index b8696234..c159e0ab 100644 --- a/sdk/test/gateway.test.ts +++ b/sdk/test/gateway.test.ts @@ -226,27 +226,72 @@ describe("Gateway Tests", () => { }); it("should get orders", async () => { + const mockOrder = { + gatewayAddress: ZeroAddress, + baseTokenAddress: TBTC_ADDRESS, + txid: "", + status: false, + timestamp: 0, + tokens: "0", + satoshis: 0, + fee: 0, + txProofDifficultyFactor: 0, + satsToConvertToEth: 0, + }; nock(`${MAINNET_GATEWAY_BASE_URL}`) .get(`/orders/${ZeroAddress}`) - .reply(200, [{ - gatewayAddress: ZeroAddress, - baseTokenAddress: TBTC_ADDRESS, - txid: "", - status: true, - timestamp: 0, - tokens: "", - satoshis: 0, - fee: 0, - txProofDifficultyFactor: 0, - strategyAddress: "", - satsToConvertToEth: 0, - outputEthAmount: "0", - outputTokenAddress: SOLVBTC_ADDRESS, - outputTokenAmount: "0", - }]); + .reply(200, [ + // staking - success + { + ...mockOrder, + satoshis: 1000, + fee: 0, + status: true, + strategyAddress: ZeroAddress, + outputTokenAmount: "2000", + outputTokenAddress: SOLVBTC_ADDRESS, + }, + // staking - pending + { + ...mockOrder, + satoshis: 1000, + fee: 0, + strategyAddress: ZeroAddress, + }, + // staking - failed + { + ...mockOrder, + satoshis: 1000, + fee: 0, + status: true, + strategyAddress: ZeroAddress, + }, + // swapping - pending + { + ...mockOrder, + satoshis: 1000, + fee: 0, + }, + // swapping - success + { + ...mockOrder, + satoshis: 1000, + fee: 0, + status: true + }, + ]); const gatewaySDK = new GatewaySDK("bob"); const orders = await gatewaySDK.getOrders(ZeroAddress); - assert.lengthOf(orders, 1); + assert.lengthOf(orders, 5); + + assert.strictEqual(orders[0].getAmount(), "2000"); + assert.strictEqual(orders[1].getAmount(), undefined); + assert.strictEqual(orders[2].getAmount(), 1000); + assert.strictEqual(orders[3].getAmount(), 1000); + assert.strictEqual(orders[4].getAmount(), 1000); + + assert.strictEqual(orders[0].getToken()!.address, SOLVBTC_ADDRESS); + assert.strictEqual(orders[1].getToken(), undefined); }); });