Skip to content

Commit

Permalink
Merge pull request #350 from bob-collective/feat/api-fields
Browse files Browse the repository at this point in the history
feat: add more api fields to gateway
  • Loading branch information
gregdhill authored Sep 16, 2024
2 parents 89fb69b + 7fefcb2 commit 17e166d
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 19 deletions.
4 changes: 2 additions & 2 deletions sdk/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@gobob/bob-sdk",
"version": "2.2.9",
"version": "2.3.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
Expand Down
24 changes: 16 additions & 8 deletions sdk/src/gateway/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
GatewayStartOrder,
GatewayStrategy,
EvmAddress,
GatewayTokensData,
} from "./types";
import { SYMBOL_LOOKUP, ADDRESS_LOOKUP } from "./tokens";
import { createBitcoinPsbt } from "../wallet";
Expand Down Expand Up @@ -80,7 +81,7 @@ export class GatewayApiClient {
GatewayQuoteParams,
"amount" | "fromChain" | "fromToken" | "fromUserAddress" | "toUserAddress"
>,
): Promise<GatewayQuote> {
): Promise<GatewayQuote & GatewayTokensData> {
const isMainnet =
params.toChain === ChainId.BOB ||
(typeof params.toChain === "string" && params.toChain.toLowerCase() === Chain.BOB);
Expand All @@ -97,7 +98,7 @@ export class GatewayApiClient {
throw new Error("Invalid output chain");
}

let outputToken = "";
let outputTokenAddress = "";
let strategyAddress: string | undefined;

const toToken = params.toToken.toLowerCase();
Expand All @@ -106,16 +107,16 @@ export class GatewayApiClient {
}

if (toToken.startsWith("0x")) {
outputToken = toToken;
outputTokenAddress = toToken;
} else if (isMainnet && this.chain === Chain.BOB && SYMBOL_LOOKUP[ChainId.BOB][toToken]) {
outputToken = SYMBOL_LOOKUP[ChainId.BOB][toToken].address;
outputTokenAddress = SYMBOL_LOOKUP[ChainId.BOB][toToken].address;
} else if (isTestnet && this.chain === Chain.BOB_SEPOLIA && SYMBOL_LOOKUP[ChainId.BOB_SEPOLIA][toToken]) {
outputToken = SYMBOL_LOOKUP[ChainId.BOB_SEPOLIA][toToken].address;
outputTokenAddress = SYMBOL_LOOKUP[ChainId.BOB_SEPOLIA][toToken].address;
} else {
throw new Error("Unknown output token");
}

var url = new URL(`${this.baseUrl}/quote/${outputToken}`);
var url = new URL(`${this.baseUrl}/quote/${outputTokenAddress}`);
if (strategyAddress) {
url.searchParams.append("strategy", `${strategyAddress}`);
}
Expand All @@ -135,6 +136,8 @@ export class GatewayApiClient {
return {
...quote,
fee: quote.fee + (params.gasRefill || 0),
baseToken: ADDRESS_LOOKUP[quote.baseTokenAddress],
outputToken: ADDRESS_LOOKUP[outputTokenAddress],
};
}

Expand Down Expand Up @@ -252,11 +255,16 @@ export class GatewayApiClient {
* @param userAddress The user's EVM address.
* @returns {Promise<GatewayOrder[]>} The array of account orders.
*/
async getOrders(userAddress: EvmAddress): Promise<GatewayOrder[]> {
async getOrders(userAddress: EvmAddress): Promise<(GatewayOrder & Optional<GatewayTokensData, "outputToken">)[]> {
const response = await this.fetchGet(`${this.baseUrl}/orders/${userAddress}`);
const orders: GatewayOrderResponse[] = await response.json();
return orders.map((order) => {
return { gasRefill: order.satsToConvertToEth, ...order };
return {
gasRefill: order.satsToConvertToEth,
...order,
baseToken: ADDRESS_LOOKUP[order.baseTokenAddress],
outputToken: ADDRESS_LOOKUP[order.outputTokenAddress]
};
});
}

Expand Down
17 changes: 15 additions & 2 deletions sdk/src/gateway/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ export interface GatewayStrategyContract {
export type GatewayQuote = {
/** @description The gateway address */
gatewayAddress: EvmAddress;
/** @description The base token address (e.g. wBTC or tBTC) */
baseTokenAddress: EvmAddress;
/** @description The minimum amount of Bitcoin to send */
dustThreshold: number;
/** @description The satoshi output amount */
Expand Down Expand Up @@ -188,8 +190,8 @@ export type GatewayCreateOrderRequest = {
export type GatewayOrderResponse = {
/** @description The gateway address */
gatewayAddress: EvmAddress;
/** @description The token address */
tokenAddress: EvmAddress;
/** @description The base token address (e.g. wBTC or tBTC) */
baseTokenAddress: EvmAddress;
/** @description The Bitcoin txid */
txid: string;
/** @description True when the order was executed on BOB */
Expand All @@ -208,6 +210,12 @@ export type GatewayOrderResponse = {
strategyAddress?: EvmAddress;
/** @description The gas refill in satoshis */
satsToConvertToEth: number;
/** @description The amount of ETH received */
outputEthAmount?: number;
/** @description The output token (from strategies) */
outputTokenAddress?: EvmAddress;
/** @description The output amount (from strategies) */
outputTokenAmount?: number;
};

/** Order given by the Gateway API once the bitcoin tx is submitted */
Expand All @@ -219,6 +227,11 @@ export type GatewayOrder = Omit<
"satsToConvertToEth"
>;

export type GatewayTokensData = {
baseToken: Token,
outputToken: Token,
};

/** @dev Internal */
export type GatewayCreateOrderResponse = {
uuid: string;
Expand Down
46 changes: 40 additions & 6 deletions sdk/test/gateway.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import { ZeroAddress } from "ethers";
import nock from "nock";
import * as bitcoin from "bitcoinjs-lib";

const TBTC = SYMBOL_LOOKUP[ChainId.BOB]["tbtc"];
const TBTC_ADDRESS = TBTC.address;
const SOLVBTC = SYMBOL_LOOKUP[ChainId.BOB]["solvbtc"];
const SOLVBTC_ADDRESS = SOLVBTC.address;

describe("Gateway Tests", () => {
it("should get chains", async () => {
const gatewaySDK = new GatewaySDK("bob");
Expand All @@ -24,16 +29,19 @@ describe("Gateway Tests", () => {

const mockQuote = {
gatewayAddress: ZeroAddress,
baseTokenAddress: TBTC_ADDRESS,
dustThreshold: 1000,
satoshis: 1000,
fee: 10,
bitcoinAddress: "",
txProofDifficultyFactor: 3,
strategyAddress: ZeroAddress,
baseToken: TBTC,
outputToken: TBTC,
};

nock(`${MAINNET_GATEWAY_BASE_URL}`)
.get(`/quote/${SYMBOL_LOOKUP[ChainId.BOB]["tbtc"].address}?satoshis=1000`)
.get(`/quote/${TBTC_ADDRESS}?satoshis=1000`)
.times(5)
.reply(200, mockQuote);

Expand All @@ -57,7 +65,7 @@ describe("Gateway Tests", () => {
}), mockQuote);
assert.deepEqual(await gatewaySDK.getQuote({
toChain: "BOB",
toToken: SYMBOL_LOOKUP[ChainId.BOB]["tbtc"].address,
toToken: TBTC_ADDRESS,
toUserAddress: ZeroAddress,
amount: 1000,
}), mockQuote);
Expand All @@ -71,11 +79,11 @@ describe("Gateway Tests", () => {

// get the total available without amount
nock(`${MAINNET_GATEWAY_BASE_URL}`)
.get(`/quote/${SYMBOL_LOOKUP[ChainId.BOB]["tbtc"].address}`)
.get(`/quote/${TBTC_ADDRESS}`)
.reply(200, mockQuote);
assert.deepEqual(await gatewaySDK.getQuote({
toChain: "BOB",
toToken: SYMBOL_LOOKUP[ChainId.BOB]["tbtc"].address,
toToken: TBTC_ADDRESS,
toUserAddress: ZeroAddress,
}), mockQuote);
});
Expand Down Expand Up @@ -118,6 +126,7 @@ describe("Gateway Tests", () => {

const mockQuote = {
gatewayAddress: ZeroAddress,
baseTokenAddress: TBTC_ADDRESS,
dustThreshold: 1000,
satoshis: 1000,
fee: 10,
Expand Down Expand Up @@ -171,12 +180,12 @@ describe("Gateway Tests", () => {
.get(`/strategies`)
.reply(200, [{
strategyAddress: ZeroAddress,
inputTokenAddress: SYMBOL_LOOKUP[ChainId.BOB]["tbtc"].address,
inputTokenAddress: TBTC_ADDRESS,
strategyName: "Pell Network (tBTC)",
strategyType: "staking"
}]);
nock(`${MAINNET_GATEWAY_BASE_URL}`)
.get(`/quote/${SYMBOL_LOOKUP[ChainId.BOB]["tbtc"].address}?satoshis=1000&strategy=${ZeroAddress}`)
.get(`/quote/${TBTC_ADDRESS}?satoshis=1000&strategy=${ZeroAddress}`)
.times(4)
.reply(200, {
gatewayAddress: ZeroAddress,
Expand Down Expand Up @@ -215,4 +224,29 @@ describe("Gateway Tests", () => {
const gatewaySDK = new GatewaySDK("bob");
assert.deepEqual(await gatewaySDK.getTokenAddresses(false), [ZeroAddress]);
});

it("should get orders", async () => {
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,
}]);

const gatewaySDK = new GatewaySDK("bob");
const orders = await gatewaySDK.getOrders(ZeroAddress);
assert.lengthOf(orders, 1);
});
});

0 comments on commit 17e166d

Please sign in to comment.