diff --git a/sdk/package.json b/sdk/package.json index 71500a0c..5e837578 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@gobob/bob-sdk", - "version": "2.3.2", + "version": "2.3.3", "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { diff --git a/sdk/src/gateway/client.ts b/sdk/src/gateway/client.ts index 0072bc0a..c83272c8 100644 --- a/sdk/src/gateway/client.ts +++ b/sdk/src/gateway/client.ts @@ -14,6 +14,8 @@ import { GatewayStrategy, EvmAddress, GatewayTokensInfo, + OrderStatus, + OrderStatusType, } from "./types"; import { SYMBOL_LOOKUP, ADDRESS_LOOKUP } from "./tokens"; import { createBitcoinPsbt } from "../wallet"; @@ -137,7 +139,7 @@ export class GatewayApiClient { ...quote, fee: quote.fee + (params.gasRefill || 0), baseToken: ADDRESS_LOOKUP[quote.baseTokenAddress], - outputToken: ADDRESS_LOOKUP[outputTokenAddress], + outputToken: quote.strategyAddress ? ADDRESS_LOOKUP[outputTokenAddress] : undefined, }; } @@ -255,7 +257,7 @@ export class GatewayApiClient { * @param userAddress The user's EVM address. * @returns {Promise} The array of account orders. */ - async getOrders(userAddress: EvmAddress): Promise<(GatewayOrder & Optional)[]> { + async getOrders(userAddress: EvmAddress): Promise<(GatewayOrder & GatewayTokensInfo)[]> { const response = await this.fetchGet(`${this.baseUrl}/orders/${userAddress}`); const orders: GatewayOrderResponse[] = await response.json(); return orders.map((order) => { @@ -273,6 +275,13 @@ export class GatewayApiClient { const getTokenAddress = (): string | undefined => { return getFinal(order.baseTokenAddress, order.outputTokenAddress); } + const getConfirmations = async (esploraClient: EsploraClient, latestHeight?: number) => { + const txStatus = await esploraClient.getTransactionStatus(order.txid); + if (!latestHeight) { + latestHeight = await esploraClient.getLatestHeight(); + } + return txStatus.confirmed ? latestHeight - txStatus.block_height! + 1 : 0; + } return { gasRefill: order.satsToConvertToEth, ...order, @@ -286,14 +295,22 @@ export class GatewayApiClient { 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; - } + getConfirmations, + async getStatus(esploraClient: EsploraClient, latestHeight?: number): Promise { + const confirmations = await getConfirmations(esploraClient, latestHeight); + const hasEnoughConfirmations = confirmations >= order.txProofDifficultyFactor; + const data = { + confirmations, + confirmed: hasEnoughConfirmations + }; + return order.status + ? order.strategyAddress + ? order.outputTokenAddress + ? { status: OrderStatusType.Success, data } + : { status: OrderStatusType.Failed, data } + : { status: OrderStatusType.Success, data } + : { status: OrderStatusType.Pending, data }; + }, }; }); } diff --git a/sdk/src/gateway/index.ts b/sdk/src/gateway/index.ts index cf787bfb..4edebc63 100644 --- a/sdk/src/gateway/index.ts +++ b/sdk/src/gateway/index.ts @@ -1,2 +1,9 @@ export { GatewayApiClient as GatewaySDK } from "./client"; -export { GatewayQuoteParams, GatewayQuote, GatewayOrder, GatewayStrategyContract } from "./types"; +export { + GatewayQuoteParams, + GatewayQuote, + GatewayOrder, + GatewayStrategyContract, + OrderStatusType, + OrderStatus, +} from "./types"; diff --git a/sdk/src/gateway/types.ts b/sdk/src/gateway/types.ts index 8466d4c5..bfb90875 100644 --- a/sdk/src/gateway/types.ts +++ b/sdk/src/gateway/types.ts @@ -189,6 +189,22 @@ export type GatewayCreateOrderRequest = { satoshis: number; }; +export type OrderStatusData = { + confirmations: number; + confirmed: boolean; +}; + +export enum OrderStatusType { + Success = "Success", + Failed = "Failed", + Pending = "Pending", +} + +export type OrderStatus = + | { status: OrderStatusType.Success; data: OrderStatusData } + | { status: OrderStatusType.Failed; data: OrderStatusData } + | { status: OrderStatusType.Pending; data: OrderStatusData }; + export interface GatewayOrderResponse { /** @description The gateway address */ gatewayAddress: EvmAddress; @@ -218,6 +234,8 @@ export interface GatewayOrderResponse { outputTokenAddress?: EvmAddress; /** @description The output amount (from strategies) */ outputTokenAmount?: string; + /** @description The tx hash on the EVM chain */ + txHash?: string; /** @description Get the actual token address received */ getTokenAddress(): string | undefined; /** @description Get the actual token received */ @@ -226,6 +244,8 @@ export interface GatewayOrderResponse { getAmount(): string | number | undefined; /** @description Get the number of confirmations */ getConfirmations(esploraClient: EsploraClient, latestHeight?: number): Promise; + /** @description Get the actual order status */ + getStatus(esploraClient: EsploraClient, latestHeight?: number): Promise; }; /** Order given by the Gateway API once the bitcoin tx is submitted */ @@ -238,8 +258,10 @@ export type GatewayOrder = Omit< >; export type GatewayTokensInfo = { + /** @description The base token (e.g. wBTC or tBTC) */ baseToken: Token, - outputToken: Token, + /** @description The output token (e.g. uniBTC or SolvBTC.BBN) */ + outputToken?: Token, }; /** @dev Internal */