debridgeClient.ts•8.6 kB
import type { deBridgeOrderResponse } from "./types/deBridge";
/**
* DeBridge DLN API Client
*
* Client for interacting with deBridge DLN cross-chain swap API.
*
* @see https://api.dln.trade/v1.0/
*
* @example
* ```ts
* const client = new DeBridgeClient();
* const quote = await client.fetchQuote({
* srcChainId: "42161",
* srcToken: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
* amount: "1000000",
* dstChainId: "1",
* dstToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
* });
* ```
*/
export class DeBridgeClient {
private readonly baseUrl: string;
/**
* Creates a new DeBridge API client.
*
* @param options - Client configuration options
* @param options.baseUrl - Base URL for deBridge API (default: from env or production URL)
*/
constructor(options?: { baseUrl?: string }) {
this.baseUrl = options?.baseUrl ||
process.env.DLN_API_BASE ||
"https://deswap.debridge.finance/v1.0";
}
/**
* Fetches a cross-chain swap quote from deBridge DLN API.
*
* @param params - Quote parameters
* @param params.srcChainId - Source blockchain ID (e.g., "42161" for Arbitrum)
* @param params.srcToken - Source token contract address
* @param params.amount - Amount in token's smallest unit (wei/decimals)
* @param params.dstChainId - Destination blockchain ID (e.g., "1" for Ethereum)
* @param params.dstToken - Destination token contract address
* @param params.senderAddress - Optional wallet address (enables transaction data generation)
* @param params.dstChainTokenOutRecipient - Optional recipient address on destination chain
* @param params.dstChainTokenOutAmount - Optional output amount (default: "auto")
* @param params.referralCode - Optional referral code for rewards
* @param params.affiliateFeePercent - Optional affiliate fee in basis points
* @param params.affiliateFeeRecipient - Optional affiliate fee recipient address
*
* @returns Promise resolving to quote estimation and optional transaction data
* @throws Error if source and destination chains are the same
* @throws Error if API request fails
*/
async fetchQuote(params: {
srcChainId: string;
srcToken: string;
amount: string;
dstChainId: string;
dstToken: string;
senderAddress?: string;
dstChainTokenOutRecipient?: string;
dstChainTokenOutAmount?: string;
referralCode?: string;
affiliateFeePercent?: number;
affiliateFeeRecipient?: string;
}): Promise<deBridgeOrderResponse> {
this.validateChains(params.srcChainId, params.dstChainId);
const queryParams = this.buildQueryParams(params);
const url = `${this.baseUrl}/dln/order/create-tx?${queryParams}`;
const response = await fetch(url);
if (!response.ok) {
const errorText = await response.text();
throw new Error(
`Failed to create bridge order: ${response.statusText}. ${errorText}`,
);
}
const data: any = await response.json();
if (data.error) {
throw new Error(`DeBridge API Error: ${data.error}`);
}
if (data.tx?.data) {
data.tx.data = data.tx.data.toString();
}
return data as deBridgeOrderResponse;
}
/**
* Fetches list of supported blockchain networks.
*
* @returns Promise resolving to list of supported chains
* @throws Error if API request fails
*/
async getSupportedChains(): Promise<{
chains: Array<{ chainId: number; originalChainId: number; chainName: string }>;
totalChains: number;
}> {
const response = await fetch(`${this.baseUrl}/supported-chains-info`);
if (!response.ok) {
throw new Error(`Failed to fetch supported chains: ${response.statusText}`);
}
const data: any = await response.json();
return {
chains: data.chains || [],
totalChains: data.chains?.length || 0
};
}
/**
* Fetches token information for a specific blockchain.
*
* @param chainId - Blockchain ID (e.g., "1" for Ethereum)
* @returns Promise resolving to tokens list and count
* @throws Error if API request fails
*/
async getTokensInfo(chainId: string): Promise<{
chainId: string;
tokens: Record<string, any>;
tokenCount: number;
}> {
const response = await fetch(`${this.baseUrl}/token-list?chainId=${chainId}`);
if (!response.ok) {
throw new Error(`Failed to fetch tokens for chain ${chainId}: ${response.statusText}`);
}
const data: any = await response.json();
return {
chainId,
tokens: data.tokens || {},
tokenCount: Object.keys(data.tokens || {}).length
};
}
/**
* Fetches the status of a cross-chain order.
*
* @param orderId - Unique order identifier
* @returns Promise resolving to order status details
* @throws Error if API request fails
*/
async getOrderStatus(orderId: string): Promise<{
orderId: string;
status: string;
srcChainTxHash: string | null;
dstChainTxHash: string | null;
orderLink: string | null;
error: string | null;
fullData: any;
}> {
const response = await fetch(`${this.baseUrl}/dln/order/${orderId}/status`);
if (!response.ok) {
throw new Error(`Failed to fetch order status: ${response.statusText}`);
}
const data: any = await response.json();
return {
orderId,
status: data.status,
srcChainTxHash: data.srcChainTxHash || null,
dstChainTxHash: data.dstChainTxHash || null,
orderLink: data.orderLink || null,
error: data.error || null,
fullData: data
};
}
/**
* Validates that source and destination chains are different.
*
* @param srcChainId - Source chain ID
* @param dstChainId - Destination chain ID
* @throws Error if chains are the same
*/
private validateChains(srcChainId: string, dstChainId: string): void {
if (srcChainId === dstChainId) {
throw new Error("Source and destination chains must be different");
}
}
/**
* Builds URL query parameters for the API request.
*
* @param params - Quote parameters
* @returns URLSearchParams object
*/
private buildQueryParams(params: {
srcChainId: string;
srcToken: string;
amount: string;
dstChainId: string;
dstToken: string;
senderAddress?: string;
dstChainTokenOutRecipient?: string;
dstChainTokenOutAmount?: string;
referralCode?: string;
affiliateFeePercent?: number;
affiliateFeeRecipient?: string;
}): URLSearchParams {
const queryParams = new URLSearchParams({
srcChainId: params.srcChainId,
srcChainTokenIn: params.srcToken,
srcChainTokenInAmount: params.amount,
dstChainId: params.dstChainId,
dstChainTokenOut: params.dstToken,
dstChainTokenOutAmount: params.dstChainTokenOutAmount || "auto",
prependOperatingExpenses: "true"
});
if (params.senderAddress) {
this.addAddressParams(queryParams, params);
}
if (params.referralCode) {
queryParams.set("referralCode", params.referralCode);
}
if (this.shouldAddAffiliateFee(params)) {
queryParams.set("affiliateFeePercent", params.affiliateFeePercent!.toString());
queryParams.set("affiliateFeeRecipient", params.affiliateFeeRecipient!);
}
return queryParams;
}
/**
* Adds address-related parameters for full mode (with wallet).
*
* @param queryParams - URLSearchParams to modify
* @param params - Quote parameters with senderAddress
*/
private addAddressParams(
queryParams: URLSearchParams,
params: {
senderAddress?: string;
dstChainTokenOutRecipient?: string;
}
): void {
if (!params.senderAddress) return;
const recipient = params.dstChainTokenOutRecipient || params.senderAddress;
queryParams.set("dstChainTokenOutRecipient", recipient);
queryParams.set("senderAddress", params.senderAddress);
queryParams.set("srcChainOrderAuthorityAddress", params.senderAddress);
queryParams.set("srcChainRefundAddress", params.senderAddress);
queryParams.set("dstChainOrderAuthorityAddress", recipient);
}
/**
* Checks if affiliate fee parameters should be added.
*
* @param params - Quote parameters
* @returns True if affiliate fee should be added
*/
private shouldAddAffiliateFee(params: {
affiliateFeePercent?: number;
affiliateFeeRecipient?: string;
}): boolean {
return params.affiliateFeePercent !== undefined &&
params.affiliateFeePercent > 0 &&
!!params.affiliateFeeRecipient;
}
}