Skip to Content

Chainflip JavaScript SDK

getChains

Returns the list of available chains or destination chains for the provided sourceChain.

function getChains( sourceChain?: Chain, type?: AssetState, ): Promise<ChainData[]>; type AssetState = | "all" | "deposit" | "destination" | "depositChannel" | "vaultSwap"; type ChainData = { chain: Chain; name: string; evmChainId: number | undefined; isMainnet: boolean; requiredBlockConfirmations: number | undefined; maxRetryDurationBlocks: number | undefined; };
ParamDescriptionData type
sourceChain (optional)Chain ID that allows to filter the returned array to contain only destination chains for the given chain.Chain
type (optional)Filters the returned chains by the swap capability they support. Defaults to all.AssetState

The type argument accepts the following values:

ValueDescription
allEvery chain supported by the protocol.
depositChains that can be used as a source chain for both deposit channel and vault swaps.
destinationChains that can be used as a destination chain.
depositChannelChains that support deposit channel swaps.
vaultSwapChains that support vault swaps.

If a sourceChain is provided, it returns the supported swap destination chains for that specific source chain.

Chain is one of Ethereum, Arbitrum, Bsc, Bitcoin, Solana, Assethub or Tron.

Polkadot was removed as a supported chain in version 1.11. Use Assethub and its DOT asset instead.

Example

import { Chains } from "@chainflip/sdk/swap"; console.log(await swapSDK.getChains(Chains.Assethub));
// console output: [ { chain: "Ethereum", name: "Ethereum", evmChainId: 1, isMainnet: true, requiredBlockConfirmations: 5, maxRetryDurationBlocks: 0, }, { chain: "Bitcoin", name: "Bitcoin", evmChainId: undefined, isMainnet: true, requiredBlockConfirmations: 2, maxRetryDurationBlocks: 0, }, ];

getAssets

Fetches the list of available assets for the given chain.

function getAssets(chain?: Chain, type?: AssetState): Promise<AssetData[]>; type AssetData = { /** internal id of the asset, e.g. `SolUsdc` for USDC on Solana */ chainflipId: ChainflipId; asset: Asset; chain: Chain; contractAddress: string | undefined; decimals: number; name: string; symbol: Asset; isMainnet: boolean; minimumSwapAmount: string; maximumSwapAmount: string | null; minimumEgressAmount: string; };
ParamDescriptionData type
chain (optional)The ID of the chainChain
type (optional)Filters the returned assets by the swap capability they support. Defaults to all.AssetState

The type argument accepts the same values as the getChains one.

Example

import { Chains } from "@chainflip/sdk/swap"; console.log(await swapSDK.getAssets(Chains.Ethereum));
// console output: [ { chainflipId: 'Eth', asset: 'ETH', chain: 'Ethereum', contractAddress: undefined, decimals: 18, name: 'Ether', symbol: 'ETH', isMainnet: true, minimumSwapAmount: '10000000000000000', maximumSwapAmount: null, minimumEgressAmount: '1' }, ..., { chainflipId: 'Usdc', asset: 'USDC', chain: 'Ethereum', contractAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', decimals: 6, name: 'USDC', symbol: 'USDC', isMainnet: true, minimumSwapAmount: '20000000', maximumSwapAmount: null, minimumEgressAmount: '1' }, ]

getQuoteV2

Fetches the quotes for swapping tokens based on the provided quoteRequest and an options argument. Currently quotes can be either a regular quotes or a DCA quotes.

function getQuoteV2( quoteRequest: QuoteRequest, options?: RequestOptions, ): Promise<QuoteResponseV2>; type QuoteRequest = { srcChain: Chain; srcAsset: Asset; destChain: Chain; destAsset: Asset; amount: string; isVaultSwap?: boolean; isOnChain?: boolean; brokerCommissionBps?: number; brokerAccount?: `cF${string}`; affiliateBrokers?: { account: string; commissionBps: number }[]; ccmParams?: { gasBudget: string; messageLengthBytes: number }; }; type RequestOptions = { signal?: AbortSignal; };
ParamDescriptionData type
quoteRequestObject containing the quote request parameters. You can find the interface definition below.Object
options (optional)Options related to the HTTP request.{ signal?: AbortSignal }

The quoteRequest object describes the swap for which a quote is returned.

ParamDescriptionData type
srcChainSource chain for the swapChain
destChainDestination chain for the swapChain
srcAssetAsset to be swapped from the source chainAsset
destAssetAsset to be received on the destination chainAsset
amountAmount of the source token to be swapped, represented in the base unit of the source tokenstring
brokerCommissionBps (optional)Commission charged by the broker in basis points. If given, the value will be used instead of the brokerCommissionBps passed when initializing the SDK instancenumber
affiliateBrokers (optional)Array of objects describing the affiliate brokers that charge a commissionArray
isVaultSwap (optional)Flag for requesting a quote for a vault swap. Network related fees can be different for vault swaps compared to channel swapsboolean
isOnChain (optional)Flag for requesting a quote for an internal swap that is executed entirely on the State Chain, without a deposit or an egressboolean
brokerAccount (optional)Chainflip broker SS58 address the quote is attributed to. Overrides the brokerAccount option passed when initializing the SDK. Brokers are encouraged to set this so their quotes can be attributed to themstring
ccmParams (optional)Additional params for requesting a quote for a CCM swap. This is only used to increase the accuracy of the broadcast fee estimation for CCM swaps. A rough estimation of the values is good enough if the actual values are unknown at this stage.Object

Result type

type QuoteResponseV2 = QuoteRequest & { quotes: Quote[]; /** id of the quote request, include it when reporting an issue with a quote */ requestId?: string; }; type Quote = RegularQuote | DCAQuote; type BoostQuote = NonNullable<Quote["boostQuote"]>; interface RegularQuote extends QuoteDetails { type: "REGULAR"; /** only present if there is a boost opportunity for the requested swap amount */ boostQuote?: Omit<RegularQuote, "boostQuote"> & BoostedQuoteDetails; } interface DCAQuote extends QuoteDetails { type: "DCA"; dcaParams: { numberOfChunks: number; chunkIntervalBlocks: number; }; /** only present if there is a boost opportunity for the requested swap amount */ boostQuote?: Omit<DCAQuote, "boostQuote"> & BoostedQuoteDetails; } interface QuoteDetails { srcAsset: { chain: Chain; asset: Asset }; destAsset: { chain: Chain; asset: Asset }; isVaultSwap: boolean; isOnChain: boolean; ccmParams?: { gasBudget: string; messageLengthBytes: number }; depositAmount: string; intermediateAmount?: string; egressAmount: string; includedFees: SwapFee[]; poolInfo: PoolInfo[]; /** recommended tolerance to prevent refunds while protecting against big price movements based on current market conditions */ recommendedSlippageTolerancePercent: number; /** recommended live price slippage tolerance to help improve the execution price of a swap based on the global index price. If undefined, live price protection is not available for the asset pair */ recommendedLivePriceSlippageTolerancePercent: number | undefined; /** recommended amount of minutes to retry a swap that has failed to meet the provided slippage tolerance */ recommendedRetryDurationMinutes: number; /** see below */ lowLiquidityWarning: boolean | undefined; /** total estimated time until destination asset is received by the user */ estimatedDurationSeconds: number; estimatedDurationsSeconds: { /** estimated time for a deposit to be witnessed */ deposit: number; /** estimated time for a swap to be fully executed */ swap: number; /** estimated time until the output transaction is included in a block */ egress: number; }; /** estimated price of the swap at amm level (does not include deposit/broadcast fee) */ estimatedPrice: string; } type BoostedQuoteDetails = { /** estimated fee (in bps) that the user has to pay (from the deposit amount) to get this swap boosted */ estimatedBoostFeeBps: number; /** maximum boost fee (in bps) that is set on the deposit channel or vault swap */ maxBoostFeeBps: number; }; type SwapFee = { type: "NETWORK" | "INGRESS" | "EGRESS" | "BROKER" | "BOOST" | "REFUND"; chain: Chain; asset: Asset; amount: string; }; type PoolInfo = { baseAsset: { chain: Chain; asset: Asset }; quoteAsset: { chain: Chain; asset: Asset }; fee: { chain: Chain; asset: Asset; amount: string }; };

A boostQuote is a full quote for the same swap executed through the boost pools. Pass it to requestDepositAddressV2 or encodeVaultSwapData instead of the parent quote to boost the swap. It is not nested any deeper: a boostQuote has no boostQuote of its own.

lowLiquidityWarning: This value is true if the difference in the chainflip swap rate (excluding fees) is lower than the global index rate of the swap by more than a certain threshold (currently set to 5%). This suggets there is not enough liquidity in the pool.

The intermediate amount is the value of the first swap leg. In this case, BTC > ETH requires both BTC/USDC and USDC/ETH pools (or legs). Learn more about Chainflip’s $USDC Denominated Pools.

Example

import { SwapSDK, Chains, Assets } from "@chainflip/sdk/swap"; const quoteRequest = { srcChain: Chains.Ethereum, srcAsset: Assets.USDC, destChain: Chains.Solana, destAsset: Assets.SOL, amount: (50e6).toString(), // 50 USDC brokerCommissionBps: 100, // 100 basis point = 1% affiliateBrokers: [ { account: "cFM8kRvLBXagj6ZXvrt7wCM4jGmHvb5842jTtXXg3mRHjrvKy", commissionBps: 50, }, ], }; const swapSDK = new SwapSDK({ enabledFeatures: { // enable DCA V2 feature when initiating the SDK to receive DCA quotes dcaV2: true, }, }); console.log(await swapSDK.getQuoteV2(quoteRequest));
// console output: { srcChain: 'Ethereum', srcAsset: 'USDC', destChain: 'Solana', destAsset: 'SOL', amount: '50000000', requestId: '3f1a9c62-7b4e-4c10-9d5f-2a8e6b0c4d13', quotes: [ { egressAmount: '309311018', recommendedSlippageTolerancePercent: 0.5, recommendedLivePriceSlippageTolerancePercent: 0.125, recommendedRetryDurationMinutes: 5, includedFees: [ { type: 'INGRESS', chain: 'Ethereum', asset: 'USDC', amount: '1395704' }, { type: 'NETWORK', chain: 'Ethereum', asset: 'USDC', amount: '48604' }, { type: 'BROKER', chain: 'Ethereum', asset: 'USDC', amount: '728335' }, { type: 'EGRESS', chain: 'Solana', asset: 'SOL', amount: '14000' }, ], lowLiquidityWarning: true, poolInfo: [ { baseAsset: { chain: 'Solana', asset: 'SOL' }, quoteAsset: { chain: 'Ethereum', asset: 'USDC' }, fee: { chain: 'Ethereum', asset: 'USDC', amount: '24302' }, }, ], estimatedDurationsSeconds: { deposit: 90, swap: 12, egress: 90.6 }, estimatedDurationSeconds: 192.6, estimatedPrice: '0.00636414974511718059', type: 'REGULAR', srcAsset: { chain: 'Ethereum', asset: 'USDC' }, destAsset: { chain: 'Solana', asset: 'SOL' }, depositAmount: '50000000', isVaultSwap: false, isOnChain: false, }, { egressAmount: '309311018', recommendedSlippageTolerancePercent: 0.5, recommendedLivePriceSlippageTolerancePercent: 0.125, recommendedRetryDurationMinutes: 5, includedFees: [ { type: 'INGRESS', chain: 'Ethereum', asset: 'USDC', amount: '1395704' }, { type: 'NETWORK', chain: 'Ethereum', asset: 'USDC', amount: '48604' }, { type: 'BROKER', chain: 'Ethereum', asset: 'USDC', amount: '728335' }, { type: 'EGRESS', chain: 'Solana', asset: 'SOL', amount: '14000' }, ], lowLiquidityWarning: true, poolInfo: [ { baseAsset: { chain: 'Solana', asset: 'SOL' }, quoteAsset: { chain: 'Ethereum', asset: 'USDC' }, fee: { chain: 'Ethereum', asset: 'USDC', amount: '24302' }, }, ], estimatedDurationsSeconds: { deposit: 90, swap: 142, egress: 90.6 }, estimatedDurationSeconds: 192.6, estimatedPrice: '0.00636414974511718059', type: 'DCA', srcAsset: { chain: 'Ethereum', asset: 'USDC' }, destAsset: { chain: 'Solana', asset: 'SOL' }, depositAmount: '50000000', isVaultSwap: false, isOnChain: false, dcaParams: { chunkIntervalBlocks: 2, numberOfChunks: 22, }, }, ], }

requestDepositAddressV2

Chainflip allows to open a deposit channel for a given set of swap details. This deposit channel is assigned to a unique address on the source chain of the swap and allows users to initiate the swap by sending the source asset to this address. Deposit channels expire after a fixed period of time (24hs).

Once a deposit channel is open, the user can send the funds from any wallet to its deposit address to initiate the swap process. No need to connect a wallet. Compared to vault swaps, using deposit channels can be cheaper for the user because it doesn’t require token approvals.

Learn more about deposit channels in the Deposit Channels & Brokers documentation.

Requests a deposit address for the given DepositAddressRequestV2. This method uses the broker_request_swap_deposit_address RPC method of the configured Broker API to open a deposit channel.

function requestDepositAddressV2( depositAddressRequest: DepositAddressRequestV2, ): Promise<DepositAddressResponseV2>; type DepositAddressRequestV2 = { quote: Quote | BoostQuote; destAddress: string; fillOrKillParams: FillOrKillParams; srcAddress?: string; brokerCommissionBps?: number; affiliateBrokers?: { account: `cF${string}` | `0x${string}`; commissionBps: number; }[]; ccmParams?: CcmParams; };

The depositAddressRequest object describes the swap for which a deposit address is requested.

ParamDescriptionData type
quoteThe object returned by getQuoteV2. This object will be used to set the input and output assets, the DCA parameters (if it is a DCA quote), and the boost parameters (if it is a boost quote).Quote
destAddressAddress where the swapped tokens will be sent to on the destination chain.string
fillOrKillParamsParameters to set a minimum accepted price. This protects the swapper against price changes between a quote and the execution of a swap.Object
srcAddress (optional)Address that sends the deposit. It is validated against the source chain and simply echoed back in the response.string
brokerCommissionBps (optional)Commission charged by the broker creating the channel, in basis points. This option is only available when the SDK is initialized with a broker URLnumber
affiliateBrokers (optional)An array of objects defining affiliate accounts that take a commission in addition to brokerCommissionBps. This option is only available when the SDK is initialized with a broker URLArray
ccmParams (optional)Optional parameters for passing a message to a reciever contract/program on the destination chain.Object

The method throws if the quote was requested with isVaultSwap or isOnChain set, since neither is executed through a deposit channel. It also throws if ccmParams is set but the quote was requested without ccmParams, or vice versa.

Result type

type DepositAddressResponseV2 = { srcChain: Chain; srcAsset: Asset; destChain: Chain; destAsset: Asset; amount: string; destAddress: string; ccmParams?: CcmParams; maxBoostFeeBps?: number; srcAddress?: string; dcaParams?: { numberOfChunks: number; chunkIntervalBlocks: number; }; fillOrKillParams: FillOrKillParams; depositChannelId: string; depositAddress: string; brokerCommissionBps: number; affiliateBrokers: { account: string; commissionBps: number }[]; depositChannelExpiryBlock: bigint; estimatedDepositChannelExpiryTime: number | undefined; channelOpeningFee: bigint; };

The fillOrKillParams returned here are the ones that were passed in, not the values that were submitted to the protocol. The SDK converts a slippageTolerancePercent into a minimum price and a retryDurationMinutes into a number of blocks before submitting them.

Configure your Broker API

If you want to earn broker fees or use affiliates, you need to run your own Broker API. You can configure the URL of your Broker API when initializing the SDK.

import { SwapSDK } from "@chainflip/sdk/swap"; const sdk = new SwapSDK({ network: "perseverance", broker: { url: "https://my.broker.io" }, });

Example

Regular Deposit Address

import { Chains, Assets, type DepositAddressRequestV2, } from "@chainflip/sdk/swap"; const { quotes } = await swapSDK.getQuoteV2({ srcChain: Chains.Ethereum, srcAsset: Assets.ETH, destChain: Chains.Bitcoin, destAsset: Assets.BTC, amount: (1.5e18).toString(), // 1.5 ETH }); const quote = quotes.find((quote) => quote.type === "REGULAR"); if (!quote) throw new Error("No quote"); const swapDepositAddressRequest: DepositAddressRequestV2 = { quote, destAddress: "bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq", fillOrKillParams: { refundAddress: "0xa56A6be23b6Cf39D9448FF6e897C29c41c8fbDFF", // address to which assets are refunded retryDurationMinutes: quote.recommendedRetryDurationMinutes, // use recommended retry duration from quote slippageTolerancePercent: quote.recommendedSlippageTolerancePercent, // use recommended slippage tolerance from quote livePriceSlippageTolerancePercent: quote.recommendedLivePriceSlippageTolerancePercent, // use recommended live price slippage tolerance from quote }, brokerCommissionBps: 100, // 100 basis point = 1% affiliateBrokers: [ { account: "cFM8kRvLBXagj6ZXvrt7wCM4jGmHvb5842jTtXXg3mRHjrvKy", commissionBps: 50, }, ], // total commission 150 bps = 1.5% }; console.log(await swapSDK.requestDepositAddressV2(swapDepositAddressRequest));
// console output: { srcAsset: 'ETH', srcChain: 'Ethereum', destAsset: 'BTC', destChain: 'Bitcoin', srcAddress: undefined, destAddress: 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq', dcaParams: undefined, fillOrKillParams: { refundAddress: '0xa56A6be23b6Cf39D9448FF6e897C29c41c8fbDFF', retryDurationMinutes: 5, slippageTolerancePercent: 0.5, livePriceSlippageTolerancePercent: 0.125 }, maxBoostFeeBps: 0, ccmParams: undefined, amount: '1500000000000000000', // 1.5 ETH depositChannelId: '6739624-Ethereum-2562', // Identifies the deposit channel for this swap depositAddress: '0x6284c66ebf4110ae3cc4da45fb9b1c2143417092', // Address where funds need to be deposited to start the swap brokerCommissionBps: 100, affiliateBrokers: [ { account: 'cFM8kRvLBXagj6ZXvrt7wCM4jGmHvb5842jTtXXg3mRHjrvKy', commissionBps: 50 } ], depositChannelExpiryBlock: 21930372n, estimatedDepositChannelExpiryTime: 1630000000, // Estimated expiry time of the deposit channel channelOpeningFee: 0n }

Boostable Deposit Address

Check the Introduction to Boost section for more information about the Boost feature.

import { Chains, Assets } from "@chainflip/sdk/swap"; const { quotes } = await swapSDK.getQuoteV2({ srcChain: Chains.Bitcoin, srcAsset: Assets.BTC, destChain: Chains.Ethereum, destAsset: Assets.ETH, amount: (1.5e8).toString(), // 1.5 BTC }); // `boostQuote` may be undefined depending on asset and liquidity conditions const regularBoostQuote = quotes.find( (quote) => quote.type === "REGULAR", )?.boostQuote; if (!regularBoostQuote) throw new Error("No boost quote"); const swapDepositAddressRequest = { quote: regularBoostQuote, destAddress: "0xa56A6be23b6Cf39D9448FF6e897C29c41c8fbDFF", fillOrKillParams: { refundAddress: "bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq", retryDurationBlocks: 100, slippageTolerancePercent: quote.recommendedSlippageTolerancePercent, livePriceSlippageTolerancePercent: quote.recommendedLivePriceSlippageTolerancePercent, }, }; console.log(await swapSDK.requestDepositAddressV2(swapDepositAddressRequest));

DCA deposit address

Check the Dollar Cost Average section for more information about the DCA feature.

import { Chains, Assets } from "@chainflip/sdk/swap"; const { quotes } = await swapSDK.getQuoteV2({ srcChain: Chains.Ethereum, srcAsset: Assets.ETH, destChain: Chains.Bitcoin, destAsset: Assets.BTC, amount: (1.5e18).toString(), // 1.5 ETH }); // ❗ the dcaV2 feature must be enabled when the SDK is instantiated to receive DCA quotes ❗ const quote = quotes.find((quote) => quote.type === "DCA"); if (!quote) throw new Error("No DCA quote"); const swapDepositAddressRequest = { quote, destAddress: "bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq", brokerCommissionBps: 100, fillOrKillParams: { refundAddress: "0xa56A6be23b6Cf39D9448FF6e897C29c41c8fbDFF", retryDurationBlocks: 100, slippageTolerancePercent: quote.recommendedSlippageTolerancePercent, livePriceSlippageTolerancePercent: quote.recommendedLivePriceSlippageTolerancePercent, }, }; console.log(await swapSDK.requestDepositAddressV2(swapDepositAddressRequest));

encodeVaultSwapData

Vault swaps are initiated by sending a transaction that encodes the details of the swap in the transaction data to the Chainflip vault on the source chain directly. This allows to initiate swaps without waiting for a deposit address to be generated.

Vault swap transactions need to encode the swap details in a specific format supported by the protocol. The user needs to sign and submit this transaction to initiate a swap. Therefore, applications usually require users to connect their wallet to initiate vault swaps.

Compared to deposit channel swaps, the vault swap process is similar to the swap process of other decentralized exchanges and therefore might be easier to integrate into existing applications.

Learn more about vault swaps in the Vault Swaps documentation.

Returns the unsigned transaction data for the given VaultSwapRequest. This method uses the broker_request_swap_parameter_encoding RPC method of the configured Broker API.

function encodeVaultSwapData( vaultSwapRequest: VaultSwapRequest, ): Promise<VaultSwapResponse>; type VaultSwapRequest = { quote: Quote | BoostQuote; destAddress: string; fillOrKillParams: FillOrKillParams; srcAddress?: string; brokerAccount?: `cF${string}`; brokerCommissionBps?: number; affiliateBrokers?: { account: `cF${string}` | `0x${string}`; commissionBps: number; }[]; ccmParams?: CcmParams; extraParams?: { seed?: `0x${string}` }; };

The vaultSwapRequest object describes the swap for which the vault swap data is encoded.

ParamDescriptionData type
quoteThe object returned by getQuoteV2. This object will be used to set the input and output assets, the DCA parameters (if it is a DCA quote), and the boost parameters (if it is a boost quote).Quote
destAddressAddress where the swapped tokens will be sent to on the destination chain.string
fillOrKillParamsParameters to set a minimum accepted price. This allows to protect against price changes between a quote and the execution of a swap (also known as slippage protection).Object
srcAddress (optional)Address that will sign and send the vault swap transaction. This parameter is required to encode vault swaps starting from Solana.string
brokerAccount (optional)Broker account that recieves the commission for the swap.string
brokerCommissionBps (optional)Commission charged by the broker of the swap, in basis points. This option is only available when setting a brokerAccount or the SDK is initialized with a broker URLnumber
affiliateBrokers (optional)An array of objects defining affiliate accounts that take a commission in addition to brokerCommissionBps. This option is only available when setting a brokerAccount or the SDK is initialized with a broker URLArray
ccmParams (optional)Optional parameters for passing a message to a reciever contract/program on the destination chain.Object
extraParams (optional)Additional data that is required for encoding vault swap transaction data for specific chains. For swaps starting from Solana, a seed can be set optionally. It must be a 0x-prefixed, 32 byte hex string. If no seed is set, a random one will be generated on the fly.Object

The quote must have been requested with isVaultSwap: true, otherwise the method throws. The fees of a vault swap differ from the fees of a deposit channel swap, so a quote for one cannot be used for the other.

Result type

The shape of the response depends on the source chain of the swap.

type VaultSwapResponse = | { chain: "Bitcoin"; nulldataPayload: string; depositAddress: string; } | { chain: "Ethereum" | "Arbitrum" | "Bsc"; calldata: string; value: bigint; to: string; sourceTokenAddress?: string | undefined; } | { chain: "Tron"; calldata: string; value: bigint; to: string; /** swap parameters that must be attached to the transaction as a note */ note: string; sourceTokenAddress?: string | undefined; } | { chain: "Solana"; programId: string; accounts: { pubkey: string; isSigner: boolean; isWritable: boolean; }[]; data: string; };

Example: Ethereum vault swap

import { Chains, Assets } from "@chainflip/sdk/swap"; import { getDefaultProvider, Wallet, Contract } from "ethers"; const wallet = new Wallet( process.env.SECRET_KEY as string, getDefaultProvider("sepolia"), ); const { quotes } = await swapSDK.getQuoteV2({ srcChain: Chains.Ethereum, srcAsset: Assets.USDC, destChain: Chains.Bitcoin, destAsset: Assets.BTC, isVaultSwap: true, amount: (500e6).toString(), // 500 USDC }); const quote = quotes.find((quote) => quote.type === "REGULAR"); if (!quote) throw new Error("No quote"); const vaultSwapRequest = { quote, srcAddress: wallet.address, destAddress: "bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq", fillOrKillParams: { refundAddress: wallet.address, // address to which assets are refunded retryDurationBlocks: 100, // 100 blocks * 6 seconds = 10 minutes before deposits are refunded slippageTolerancePercent: quote.recommendedSlippageTolerancePercent, // use recommended slippage tolerance from quote livePriceSlippageTolerancePercent: quote.recommendedLivePriceSlippageTolerancePercent, // use recommended live price slippage tolerance from quote }, }; const vaultSwapData = await swapSDK.encodeVaultSwapData(vaultSwapRequest); console.log(vaultSwapData); // `VaultSwapResponse` is a union - narrow it on `chain` to get the fields for // the source chain of the swap if (vaultSwapData.chain !== "Ethereum") throw new Error("Invalid chain"); // approve chainflip vault to spend erc20 token if (vaultSwapData.sourceTokenAddress) { const sourceTokenContract = new Contract( vaultSwapData.sourceTokenAddress, ["function approve(address spender, uint256 value) returns (bool)"], wallet, ); const approvalTx = await sourceTokenContract.approve( vaultSwapData.to, quote.depositAmount, ); await approvalTx.wait(); } const swapTx = await wallet.sendTransaction({ to: vaultSwapData.to, data: vaultSwapData.calldata, value: vaultSwapData.value, }); const receipt = await swapTx.wait(); // wait for transaction to be included in a block if (!receipt) throw new Error("Transaction was not mined"); // status will be available after the transaction is included in a block const status = await swapSDK.getStatusV2({ id: receipt.hash });
// console output: { chain: 'Ethereum', to: '0x36ead71325604dc15d35fae584d7b50646d81753', calldata: '0xdd68734500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000014cb583c817964a2c527608f8b813a4c9bddb559a9000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e0064000000cb583c817964a2c527608f8b813a4c9bddb559a945f572d5916d163cccbef285e25d4bde2400000000000000000000000000000000009e8d88ae895c9b37b2dead9757a3452f7c2299704d91ddfa444d87723f94fe0c0000000000', value: 0n, sourceTokenAddress: '0x1c7d4b196cb0c7b01d743fbc6116a902379c7238' }

Example: Bitcoin vault swap (Boosted)

import { Chains, Assets } from "@chainflip/sdk/swap"; import Client from "bitcoin-core"; import * as bitcoin from "bitcoinjs-lib"; import ECPairFactory from "ecpair"; import * as secp256k1 from "tiny-secp256k1"; const keypair = ECPairFactory(secp256k1).fromWIF( process.env.WALLET_WIF as string, ); const network = bitcoin.networks.testnet; const walletAddress = bitcoin.payments.p2wpkh({ pubkey: Buffer.from(keypair.publicKey), network, }).address!; bitcoin.initEccLib(secp256k1); const { quotes } = await swapSDK.getQuoteV2({ srcChain: Chains.Bitcoin, srcAsset: Assets.BTC, destChain: Chains.Ethereum, destAsset: Assets.ETH, isVaultSwap: true, amount: (0.015e8).toString(), // 0.015 BTC }); // `boostQuote` may be undefined depending on asset and liquidity conditions const quote = quotes.find((quote) => quote.type === "REGULAR")?.boostQuote; if (!quote) throw new Error("No boost quote"); const vaultSwapRequest = { quote, srcAddress: walletAddress, destAddress: "0xa56A6be23b6Cf39D9448FF6e897C29c41c8fbDFF", fillOrKillParams: { refundAddress: walletAddress, retryDurationBlocks: 100, slippageTolerancePercent: quote.recommendedSlippageTolerancePercent, livePriceSlippageTolerancePercent: quote.recommendedLivePriceSlippageTolerancePercent, }, }; const vaultSwapData = await swapSDK.encodeVaultSwapData(vaultSwapRequest); console.log(vaultSwapData); // `VaultSwapResponse` is a union - narrow it on `chain` to get the fields for // the source chain of the swap if (vaultSwapData.chain !== "Bitcoin") throw new Error("Invalid chain"); export const rpcClient = new Client({ host: "https://bitcoin-testnet-rpc.publicnode.com", }); const inputUtxo = { txId: "4403c8d3b752a3aa0757031239a82ce92d67fadc1bb4a35f80ace660499edd55", outIndex: 2, }; const inputTx = bitcoin.Transaction.fromHex( await rpcClient.command("getrawtransaction", inputUtxo.txId), ); const txFeeSats = 1000n; const tx = new bitcoin.Psbt({ network }) .addInput({ hash: inputTx.getHash(), index: inputUtxo.outIndex, nonWitnessUtxo: inputTx.toBuffer(), sequence: 0xfffffffd, // enable replace-by-fee }) .addOutput({ // first output needs to spend assets to deposit address address: vaultSwapData.depositAddress, value: BigInt(quote.depositAmount), }) .addOutput({ // second output needs to return encoded vault details using the OP_RETURN opcode script: bitcoin.payments.embed({ data: [ Buffer.from(vaultSwapData.nulldataPayload.replace("0x", ""), "hex"), ], network, }).output!, value: 0n, }) .addOutput({ // third output defines the refund address for the swap address: walletAddress, value: inputTx.outs[inputUtxo.outIndex].value - BigInt(quote.depositAmount) - txFeeSats, }) .signInput(0, { publicKey: Buffer.from(keypair.publicKey), sign: (hash) => Buffer.from(keypair.sign(hash)), }) .finalizeAllInputs() .extractTransaction(); await rpcClient.command("sendrawtransaction", tx.toHex()); // status will be available once the transaction is in the mempool const status = await swapSDK.getStatusV2({ id: tx.getId() });
// console output: { chain: 'Bitcoin', nulldataPayload: '0x0001a56a6be23b6cf39d9448ff6e897c29c41c8fbdff640029a43fc140746a5e000000000000000001000200000000', depositAddress: 'tb1pxu6h02gundytrfvlrxtjylkqmfv072ljtqcfvwclh37cx4eq7chqq6zmng' }

The change address, which is included in the change UTXO as the third output, is mandatory because the Chainflip protocol needs a refund address in case the swap is refunded.

Per the Bitcoin protocol, the change UTXO amount cannot be zero and must be larger than Bitcoin’s dust threshold for the change address type. Therefore it is not possible to swap the entire amount of the input UTXOs, a dust amount needs to be sent to the change address.

Example: Solana vault swap

import { Chains, Assets, type VaultSwapRequest } from "@chainflip/sdk/swap"; import { randomBytes } from "crypto"; import { PublicKey, Keypair, sendAndConfirmTransaction, TransactionInstruction, Transaction, Connection, clusterApiUrl, } from "@solana/web3.js"; import bs58 from "bs58"; const keypair = Keypair.fromSecretKey( bs58.decode(process.env.SECRET_KEY_BASE58 as string), ); const { quotes } = await swapSDK.getQuoteV2({ srcChain: Chains.Solana, srcAsset: Assets.SOL, destChain: Chains.Ethereum, destAsset: Assets.ETH, isVaultSwap: true, amount: (1.5e9).toString(), // 1.5 SOL }); const quote = quotes.find((quote) => quote.type === "REGULAR"); if (!quote) throw new Error("No quote"); const vaultSwapRequest: VaultSwapRequest = { quote, srcAddress: keypair.publicKey.toBase58(), destAddress: "0xa56A6be23b6Cf39D9448FF6e897C29c41c8fbDFF", fillOrKillParams: { refundAddress: keypair.publicKey.toBase58(), retryDurationBlocks: 100, slippageTolerancePercent: quote.recommendedSlippageTolerancePercent, livePriceSlippageTolerancePercent: quote.recommendedLivePriceSlippageTolerancePercent, }, extraParams: { // the seed must be a 0x-prefixed, 32 byte hex string seed: `0x${randomBytes(32).toString("hex")}`, }, }; const vaultSwapData = await swapSDK.encodeVaultSwapData(vaultSwapRequest); console.log(vaultSwapData); // `VaultSwapResponse` is a union - narrow it on `chain` to get the fields for // the source chain of the swap if (vaultSwapData.chain !== "Solana") throw new Error("Invalid chain"); const transaction = new Transaction().add( new TransactionInstruction({ keys: vaultSwapData.accounts.map((account) => ({ pubkey: new PublicKey(account.pubkey), isSigner: account.isSigner, isWritable: account.isWritable, })), programId: new PublicKey(vaultSwapData.programId), data: Buffer.from(vaultSwapData.data.slice(2), "hex"), }), ); const signature = await sendAndConfirmTransaction( new Connection(clusterApiUrl("devnet"), "confirmed"), transaction, [keypair], ); // status will be available after the transaction is included in a block const status = await swapSDK.getStatusV2({ id: signature });
// console output: { chain: 'Solana', programId: 'DeL6iGV5RWrWh7cPoEa7tRHM8XURAaB4vPjfX5qVyuWE', data: '0xa3265ce2f3698dc4002f6859000000000100000014000000c64722ad9613851b10e26ff8118a7696a0f956f201000000006a000000000a0000009e0d6a70e12d54edf90971cc977fa26a1d3bb4b0b26e72470171c36b0006b01f000000100000000000000000000000000000000000000000000000000000000000009059e6d854b769a505d01148af212bf8cb7f8469a7153edce8dcaedd9d299125000000200000004a7b9c2f1e8d3a5b6c4f9e2d7a1b3c8f0e5d9a2c6b4f7e1d3a8b5c9f2e0d6a30', accounts: [ { pubkey: 'GpTqSHz4JzQimjfDiBgDhJzYcTonj3t6kMhKTigCKHfc', isSigner: false, isWritable: false }, ... ] }

Example: Tron vault swap

Unlike the other chains, a Tron vault swap does not encode the swap parameters into the transaction itself. The swap is a plain TRX transfer to the Chainflip vault (or a TRC-20 transfer call for tokens), and the returned note is attached to the transaction as transaction data.

import { Chains, Assets } from "@chainflip/sdk/swap"; import { TronWeb } from "tronweb"; const privateKey = process.env.TRON_SECRET_KEY as string; const tronWeb = new TronWeb({ fullHost: "https://nile.trongrid.io", // Nile testnet matches perseverance privateKey, }); const tronAddress = tronWeb.address.fromPrivateKey(privateKey) as string; const swapAmount = (50e6).toString(); // 50 TRX in sun const { quotes } = await swapSDK.getQuoteV2({ srcChain: Chains.Tron, srcAsset: Assets.TRX, destChain: Chains.Ethereum, destAsset: Assets.USDC, isVaultSwap: true, amount: swapAmount, }); const quote = quotes.find((quote) => quote.type === "REGULAR"); if (!quote) throw new Error("No quote"); const vaultSwapData = await swapSDK.encodeVaultSwapData({ quote, destAddress: "0xa56A6be23b6Cf39D9448FF6e897C29c41c8fbDFF", fillOrKillParams: { refundAddress: tronAddress, retryDurationBlocks: 100, slippageTolerancePercent: quote.recommendedSlippageTolerancePercent, livePriceSlippageTolerancePercent: quote.recommendedLivePriceSlippageTolerancePercent, }, }); console.log(vaultSwapData); // `VaultSwapResponse` is a union - narrow it on `chain` to get the fields for // the source chain of the swap if (vaultSwapData.chain !== "Tron") throw new Error("Invalid chain"); const { to, value, note, sourceTokenAddress } = vaultSwapData; let transaction; if (sourceTokenAddress) { // TRC-20 (USDT): call transfer(vault, amount) on the token contract const { transaction: trc20Tx } = await tronWeb.transactionBuilder.triggerSmartContract( sourceTokenAddress, "transfer(address,uint256)", { feeLimit: 100_000_000 }, [ { type: "address", value: to }, { type: "uint256", value: swapAmount }, ], tronAddress, ); transaction = trc20Tx; } else { // native TRX: send TRX directly to the Chainflip vault address transaction = await tronWeb.transactionBuilder.sendTrx( to, Number(value), // value is in sun (1 TRX = 1,000,000 sun) tronAddress, ); } // attach the swap parameters as a note - this is what identifies the // transaction to Chainflip transaction = await tronWeb.transactionBuilder.addUpdateData( transaction, note.slice(2), // strip the leading 0x "hex", ); const signedTx = await tronWeb.trx.sign(transaction); const receipt = await tronWeb.trx.sendRawTransaction(signedTx); // status will be available after the transaction is included in a block const status = await swapSDK.getStatusV2({ id: receipt.txid });
// console output (native TRX swap): { chain: 'Tron', to: 'TYPcfPnrhqRRbaBmeJoWmgAcbVLGrJHeLR', calldata: '0x', value: 50000000n, note: '0x0001a56a6be23b6cf39d9448ff6e897c29c41c8fbdff640029a43fc140746a5e00000000000000000100020000', sourceTokenAddress: undefined }

encodeCfParameters

It is recommended to encode Vault swaps using the SDK’s method to encode the Vault Swap data. However, some integrators might want to manually execute the smart contract call to initiate Vault Swaps.

As documented in the encoding-reference, a cf_parameters bytes variable needs to be encoded as part of the Vault Swap initiation via a smart contract call. While that can be encoded manually, the SDK provides a method to encode it for convenience.

The encodeCfParameters function returns the hex-encoded CF parameters. This method uses the broker_encode_cf_parameters RPC method of the configured Broker API.

interface EncodeCfParametersRequest { quote: Quote | BoostQuote; srcAddress?: string; destAddress: string; fillOrKillParams: FillOrKillParams; affiliateBrokers?: { account: `cF${string}` | `0x${string}`; commissionBps: number; }[]; ccmParams?: CcmParams; brokerAccount?: `cF${string}`; brokerCommissionBps?: number; } class SwapSDK { encodeCfParameters( cfParameters: EncodeCfParametersRequest, ): Promise<`0x${string}`>; }

The cfParameters object describes the swap for which the CF parameters data is encoded.

ParamDescriptionData type
quoteThe object returned by getQuoteV2 This object will be used to set the input and output assets, the DCA parameters (if it is a DCA quote), and the boost parameters (if it is a boost quote).Quote
destAddressAddress where the swapped tokens will be sent to on the destination chain.string
fillOrKillParamsParameters to set a minimum accepted price This allows to protect against price changes between a quote and the execution of a swap (also known as slippage protection).Object
srcAddress (optional)Address that will sign and send the vault swap transaction. This parameter is required to encode vault swaps starting from Solana.string
brokerAccount (optional)account that receives the commission for the swap.string
brokerCommissionBps (optional)Commission charged by the broker of the swap, in basis points. This option is only available when setting a brokerAccount or the SDK is initialized with a broker URLnumber
affiliateBrokers (optional)An array of objects defining affiliate accounts that take a commission in addition to brokerCommissionBps. This option is only available when setting a brokerAccount or the SDK is initialized with a broker URLArray
ccmParams(optional)Optional parameters for passing a message to a reciever contract/program on the destination chain.Object

Result type

The method returns bytes as a hex-encoded string that can be passed to the State Chain.

Example

import { Chains, Assets, SwapSDK } from "@chainflip/sdk/swap"; const swapSDK = new SwapSDK({ network: "perseverance" }); const { quotes } = await swapSDK.getQuoteV2({ srcChain: Chains.Ethereum, srcAsset: Assets.USDC, destChain: Chains.Bitcoin, destAsset: Assets.BTC, isVaultSwap: true, amount: (500e6).toString(), // 500 USDC }); const quote = quotes.find((quote) => quote.type === "REGULAR"); if (!quote) throw new Error("No quote"); const vaultSwapRequest = { quote, destAddress: "tb1q65700ylwuua9669umxdep76a69jy3tfh55llf5", fillOrKillParams: { refundAddress: "0x36ead71325604dc15d35fae584d7b50646d81753", // address to which assets are refunded retryDurationBlocks: 100, // 100 blocks * 6 seconds = 10 minutes before deposits are refunded slippageTolerancePercent: quote.recommendedSlippageTolerancePercent, // use recommended slippage tolerance from quote livePriceSlippageTolerancePercent: quote.recommendedLivePriceSlippageTolerancePercent, // use recommended live price slippage tolerance from quote }, }; const vaultSwapData = await swapSDK.encodeCfParameters(vaultSwapRequest); console.log(vaultSwapData);
// console output: "0x006400000036ead71325604dc15d35fae584d7b50646d81753b8b06ebc3b32569bff571d39d21918010000000000000000000000000000000000009e8d88ae895c9b37b2dead9757a3452f7c2299704d91ddfa444d87723f94fe0c000000";

getStatusV2

Fetches the status of an ongoing swap based on the provided swapStatusRequest argument.

function getStatusV2( swapStatusRequest: SwapStatusRequest, options?: RequestOptions, ): Promise<SwapStatusResponseV2>; type SwapStatusRequest = { id: string; }; type RequestOptions = { signal?: AbortSignal; };

The SwapStatusRequest object includes the following arguments:

ParamDescriptionData type
idOne of the following:
- The depositChannelId returned by requestDepositAddressV2
- The hash of the transaction that was submitted for a swap encoded with encodeVaultSwapData
- The swapId of a swap
string

Result type

The response is a discriminated union on the state property. Each state adds the fields that are known at that point of the swap lifecycle.

type SwapStatusResponseV2 = | ({ state: "WAITING" } & Waiting) | ({ state: "RECEIVING" } & Receiving) | ({ state: "SWAPPING" } & Swapping) | ({ state: "SENDING" } & Sending) | ({ state: "SENT" } & Sending) | ({ state: "COMPLETED" } & Sending) | ({ state: "FAILED" } & Sending); interface Waiting extends SwapStatusResponseCommonFields { /** this state is only possible for swaps with a deposit channel */ depositChannel: DepositChannelFields; } interface Receiving extends SwapStatusResponseCommonFields { deposit: DepositFields; } interface Swapping extends Receiving { swap: SwapFields; } interface Sending extends Receiving { swap: SwapFields; swapEgress: EgressFields | undefined; refundEgress: EgressFields | undefined; /** egress of a swap that was sent to the refund address after the destination transfer failed */ fallbackEgress: EgressFields | undefined; } interface SwapStatusResponseCommonFields { swapId: string; /** `Polkadot` is still returned for swaps that were executed before it was removed */ srcChain: Chain | "Polkadot"; srcAsset: Asset; destChain: Chain | "Polkadot"; destAsset: Asset; destAddress: string; depositChannel: DepositChannelFields | undefined; ccmParams: CcmParams | undefined; fillOrKillParams: FillOrKillParamsWithMinPrice | undefined; dcaParams: DcaParams | undefined; boost: Boost | undefined; /** the broker that opened the channel or encoded the vault swap, followed by its affiliates */ brokers: Broker[]; fees: PaidFee[]; /** total estimated time until destination asset is received by the user */ estimatedDurationSeconds: number | null | undefined; estimatedDurationsSeconds: { /** estimated time for a deposit to be witnessed */ deposit: number; /** estimated time for a swap to be fully executed */ swap: number; /** estimated time until the output transaction is included in a block */ egress: number; }; srcChainRequiredBlockConfirmations: number | null; /** timestamp of the last State Chain block that updated this swap */ lastStatechainUpdateAt: number | undefined; } interface Broker { account: string; commissionBps: string; } interface DepositChannelFields { id: string; createdAt: number; depositAddress: string; srcChainExpiryBlock: string; estimatedExpiryTime: number; expectedDepositAmount: string | undefined; isExpired: boolean; openedThroughBackend: boolean; dcaParams: DcaParams | undefined; } interface Boost { maxBoostFeeBps: number; effectiveBoostFeeBps: number | undefined; boostedAt: number | undefined; boostedBlockIndex: string | undefined; /** set when a boost was skipped for lack of boost liquidity, so the deposit waits for full confirmation */ skippedAt: number | undefined; skippedBlockIndex: string | undefined; } interface DepositFields { amount: string; txRef: string | undefined; txConfirmations: number | undefined; witnessedAt: number | undefined; witnessedBlockIndex: string | undefined; failure: Failure | undefined; failedAt: number | undefined; failedBlockIndex: string | undefined; } interface SwapFields { originalInputAmount: string; remainingInputAmount: string; swappedInputAmount: string; swappedIntermediateAmount: string; swappedOutputAmount: string; /** average delta between the oracle price and the execution price across all chunks */ livePriceExecutionDeltaPercentage: number | undefined; /** only present for regular (non-DCA) swaps */ regular: ChunkInfo | undefined; /** only present for DCA swaps */ dca: | { lastExecutedChunk: ChunkInfo | null; /** the chunk that is scheduled but has not been executed yet */ currentChunk: CurrentChunkInfo | null; executedChunks: number; remainingChunks: number; } | undefined; /** only present for swaps that were executed on the State Chain */ onChain: | { /** the account identifier of the LP for the on-chain swap */ accountId: string; /** undefined if the swap has not been executed or was fully refunded */ outputAmount: string | undefined; /** undefined if no refund occurred */ refundAmount: string | undefined; } | undefined; } type ChunkInfo = { inputAmount: string; intermediateAmount: string | undefined; outputAmount: string | undefined; scheduledAt: number; scheduledBlockIndex: string; executedAt: number | undefined; executedBlockIndex: string | undefined; retryCount: number; abortedAt: number | undefined; abortedBlockIndex: string | undefined; abortedReason: ChunkFailureReason | undefined; latestSwapRescheduledAt: number | undefined; latestSwapRescheduledBlockIndex: string | undefined; latestSwapRescheduledReason: ChunkFailureReason | undefined; }; /** a chunk that has not been executed yet only carries the fields below */ type CurrentChunkInfo = Pick< ChunkInfo, | "inputAmount" | "scheduledAt" | "scheduledBlockIndex" | "retryCount" | "latestSwapRescheduledAt" | "latestSwapRescheduledBlockIndex" | "latestSwapRescheduledReason" >; type ChunkFailureReason = | "PriceImpactLimit" | "MinPriceViolation" | "OraclePriceSlippageExceeded" | "OraclePriceStale" | "PredecessorSwapFailure" | "SafeModeActive"; interface EgressFields { amount: string; scheduledAt: number | undefined; scheduledBlockIndex: string | undefined; txRef: string | undefined; witnessedAt: number | undefined; witnessedBlockIndex: string | undefined; failure: Failure | undefined; failedAt: number | undefined; failedBlockIndex: string | undefined; /** only present for egresses that have to be manually submitted by the user */ transactionPayload: | { contract: `0x${string}`; value: string; chainId: string; data: `0x${string}`; } | undefined; } interface Failure { failedAt: number; failedBlockIndex: string; mode: string; reason: { code: string; message: string; }; } interface PaidFee { type: | "NETWORK" | "INGRESS" | "EGRESS" | "BROKER" | "BOOST" | "REFUND" | "LIQUIDITY"; chain: Chain; asset: Asset; amount: string; }

depositChannel.brokerCommissionBps, depositChannel.affiliateBrokers and depositChannel.fillOrKillParams were removed in version 2.2. Use brokers and fillOrKillParams at the root of the response instead.

Example

const swapStatusRequest = { id: "6726421-Bitcoin-34009", // depositChannelId / swapId / transactionId }; console.log(await swapSDK.getStatusV2(swapStatusRequest));
// console output: { state: 'COMPLETED', swapId: '332925', srcAsset: 'BTC', srcChain: 'Bitcoin', destAsset: 'USDC', destChain: 'Ethereum', destAddress: '0xbbfefb09727136a6c3ddd28bd4abdf259b9351c1', srcChainRequiredBlockConfirmations: 2, estimatedDurationsSeconds: { deposit: 1806, swap: 12, egress: 102 }, estimatedDurationSeconds: 1920, brokers: [ { account: 'cFJZVRaybb2PBwxTiAiRLiQfHY4KPB3RpJK22Q7Fhqk979aCH', commissionBps: 5 }, { account: 'cFK6mYjpajcwPDZ7JUsac8XUoVSJnhjL43ZMZW7YoN8HE4dD8', commissionBps: 60 }, ], fees: [ { type: 'NETWORK', chain: 'Ethereum', asset: 'USDC', amount: '52965555' }, { type: 'LIQUIDITY', chain: 'Bitcoin', asset: 'BTC', amount: '27972' }, { type: 'BROKER', chain: 'Ethereum', asset: 'USDC', amount: '343931842' }, { type: 'BOOST', chain: 'Bitcoin', asset: 'BTC', amount: '28000' }, { type: 'INGRESS', chain: 'Bitcoin', asset: 'BTC', amount: '1251' }, { type: 'EGRESS', chain: 'Ethereum', asset: 'USDC', amount: '0' }, ], depositChannel: { id: '6726421-Bitcoin-34009', createdAt: 1740415452001, depositAddress: 'bc1ppaxkhh5c65zypqtjl7s5j4lqqaund3lzaz27cwlhw7nvg0s84cwqv4q6pw', srcChainExpiryBlock: '885280', estimatedExpiryTime: 1740502800000, expectedDepositAmount: undefined, isExpired: false, openedThroughBackend: false, dcaParams: { numberOfChunks: 18, chunkIntervalBlocks: 2 }, }, deposit: { amount: '55970749', txRef: 'ba030cf99b7b01d9455c3ab87a5d87a301b67e91640fc9c76014c6e5b97986d7', witnessedAt: 1740415836000, witnessedBlockIndex: '6726483-536', }, swap: { originalInputAmount: '55970749', remainingInputAmount: '0', swappedInputAmount: '55970749', swappedIntermediateAmount: '0', swappedOutputAmount: '52568658562', dca: { lastExecutedChunk: { inputAmount: '3109487', outputAmount: '2916364871', scheduledAt: 1740416046000, scheduledBlockIndex: '6726517-2189', executedAt: 1740416058000, executedBlockIndex: '6726519-1073', retryCount: 0, }, currentChunk: null, executedChunks: 18, remainingChunks: 0, }, }, swapEgress: { amount: '52568658562', scheduledAt: 1740416058000, scheduledBlockIndex: '6726519-1074', witnessedAt: 1740416262000, witnessedBlockIndex: '6726552-640', txRef: '0xc8646625910babc04b1612526c27fbac68aacf6cc489affbae69d1d430f5fcb1', }, fillOrKillParams: { refundAddress: 'bc1qstlcaqcds8xea7dtktdn47hng7c3lq3fa7vrtm', minPrice: '92307.460172', retryDurationBlocks: 150, }, dcaParams: { numberOfChunks: 18, chunkIntervalBlocks: 2 }, boost: { effectiveBoostFeeBps: 5, maxBoostFeeBps: 0, boostedAt: 1740415836000, boostedBlockIndex: '6726483-536', }, lastStatechainUpdateAt: 1740497470820, }

Lifecycle of a swap


getSwapLimits

Fetches the lower and upper bounds for swap amounts.

function getSwapLimits(): Promise<{ minimumSwapAmounts: ChainAssetMap<bigint>; maximumSwapAmounts: ChainAssetMap<bigint | null>; }>; type ChainAssetMap<T> = { Bitcoin: { BTC: T }; Ethereum: { ETH: T; FLIP: T; USDC: T; USDT: T; WBTC: T; CBBTC: T }; Arbitrum: { ETH: T; USDC: T; USDT: T }; Solana: { SOL: T; USDC: T; USDT: T }; Assethub: { DOT: T; USDC: T; USDT: T }; Bsc: { BNB: T; USDT: T }; Tron: { TRX: T; USDT: T }; };

In order to determine if a swap amount is within the bounds, you can check to see if the amount is greater than or equal to the minimum swap amount for the given source asset and chain and less than or equal to the maximum swap amount for the asset and chain.

These values are in the base unit of the asset, i.e. satoshis for Bitcoin, wei for Ethereum, and Planck for Assethub, etc.

Funds sent in excess of the maximum swap amount are absorbed by the protocol and cannot be recovered.

Funds that fail to meet the minimum swap amount are also absorbed by the protocol and cannot be recovered.

Example

console.log(await swapSDK.getSwapLimits());
// console output (the values are protocol parameters and change over time): { minimumSwapAmounts: { Bitcoin: { BTC: 70000n }, Ethereum: { ETH: 10000000000000000n, FLIP: 4000000000000000000n, USDC: 20000000n, USDT: 20000000n, WBTC: 70000n, CBBTC: 70000n }, Arbitrum: { ETH: 10000000000000000n, USDC: 20000000n, USDT: 20000000n }, Solana: { SOL: 0n, USDC: 0n, USDT: 0n }, Assethub: { DOT: 40000000000n, USDC: 20000000n, USDT: 20000000n }, Bsc: { BNB: 10000000000000000n, USDT: 20000000000000000000n }, Tron: { TRX: 20000000n, USDT: 20000000n } }, maximumSwapAmounts: { Bitcoin: { BTC: 4000000n }, Ethereum: { ETH: 500000000000000000n, FLIP: 200000000000000000000n, USDC: 1000000000n, USDT: 1000000000n, WBTC: null, CBBTC: null }, Arbitrum: { ETH: 500000000000000000n, USDC: 1000000000n, USDT: null }, Solana: { SOL: null, USDC: null, USDT: null }, Assethub: { DOT: 2000000000000n, USDC: null, USDT: null }, Bsc: { BNB: null, USDT: null }, Tron: { TRX: null, USDT: null } } }

getBoostLiquidity

Returns the list of available boost pools and their respective depths. The returned list can be filtered by feeTierBps or asset, chain.

function getBoostLiquidity( params: | { feeTierBps?: number } | { feeTierBps?: number; asset: Asset; chain: Chain }, ): Promise<BoostPoolDepth[]>; type BoostPoolDepth = { feeTierBps: number; availableAmount: bigint; asset: Asset; chain: Chain; poolType: "LEGACY_BOOST" | "SUPPLY"; };
ParamDescriptionData type
feeTierBps (optional)Allows filtering of the returned list of boost pools based on their fee tier (bps)number
chain, asset (optional)Allows filtering of the returned list of boost pools based on their chain assetChain, Asset

poolType describes where the liquidity comes from: LEGACY_BOOST for the boost pools, and SUPPLY for lending supply pools that contribute to boosting.

feeTierBps is deprecated. All boost pools other than the 5 bps one have been removed in version 2.2, so 5 is the only accepted value and passing anything else throws.

Example: no filtering

If no chain, asset or feeTierBps is provided, it returns the full list of available boost pools with their respective depths.

console.log(await swapSDK.getBoostLiquidity());
// console output: [ // ... { availableAmount: 0n, feeTierBps: 5, chain: "Ethereum", asset: "FLIP", poolType: "LEGACY_BOOST", }, { availableAmount: 0n, feeTierBps: 5, chain: "Ethereum", asset: "USDC", poolType: "LEGACY_BOOST", }, { availableAmount: 450695350n, feeTierBps: 5, chain: "Bitcoin", asset: "BTC", poolType: "LEGACY_BOOST", }, { availableAmount: 1200000000n, feeTierBps: 5, chain: "Bitcoin", asset: "BTC", poolType: "SUPPLY", }, { availableAmount: 0n, feeTierBps: 5, chain: "Arbitrum", asset: "USDC", poolType: "LEGACY_BOOST", }, // ... ];

Example: filtering by chain asset

If a valid combination of chain, asset is provided, it returns the available boost pools for that specific chain asset.

console.log( await sdk.getBoostLiquidity({ asset: "BTC", chain: "Bitcoin", }), );
// console output: [ { availableAmount: 450695350n, feeTierBps: 5, chain: "Bitcoin", asset: "BTC", poolType: "LEGACY_BOOST", }, { availableAmount: 1200000000n, feeTierBps: 5, chain: "Bitcoin", asset: "BTC", poolType: "SUPPLY", }, ];

Example: filtering by fee tier (bps)

If a feeTierBps is provided, it returns the available boost pools for that specific fee tier.

console.log( await sdk.getBoostLiquidity({ feeTierBps: 5, }), );
// console output: [ // ... { availableAmount: 450695350n, feeTierBps: 5, chain: "Bitcoin", asset: "BTC", poolType: "LEGACY_BOOST", }, // ... ];

getChannelOpeningFees

Returns the fee in Flipperino required to open a deposit channel for a chain.

function getChannelOpeningFees(): Promise<ChainMap<bigint>>; type ChainMap<T> = { Bitcoin: T; Ethereum: T; Arbitrum: T; Solana: T; Assethub: T; Bsc: T; Tron: T; };

When a broker opens a swap deposit channel, there is a fee that must be paid from the $FLIP balance of the account on the State Chain. The fee is measured in Flipperino which is the smallest unit of $FLIP (1 $FLIP = 10^18 Flipperino). This fee is determined per chain.

Example

console.log(await swapSDK.getChannelOpeningFees());
// console output (the values are protocol parameters and change over time): { Bitcoin: 0n, Ethereum: 0n, Arbitrum: 0n, Solana: 0n, Assethub: 0n, Bsc: 0n, Tron: 0n }

getRequiredBlockConfirmations

Fetches the number of confirmations required by the protocol for each chain.

function getRequiredBlockConfirmations(): Promise<ChainMap<number | null>>; type ChainMap<T> = { Bitcoin: T; Ethereum: T; Arbitrum: T; Solana: T; Assethub: T; Bsc: T; Tron: T; };

Before a transaction to a deposit address or via smart contract call can be considered confirmed, it must be included in a certain number of blocks to limit the risk of a “reorg” event to the protocol. The number of blocks varies by chain. Some chains, like Assethub, have deterministic finality and do not require any confirmations other than being included in a block. These chains will return null for the number of confirmations required, while others will return a number.

Example

console.log(await swapSDK.getRequiredBlockConfirmations());
// console output (the values are protocol parameters and change over time): { Bitcoin: 2, Ethereum: 5, Arbitrum: 2, Solana: null, Assethub: null, Bsc: 5, Tron: 20 }

checkBoostEnabled

Returns whether boost deposits are currently enabled. The flag is read from the configuration of the Bitcoin asset.

function checkBoostEnabled(): Promise<boolean>;

Boost deposits can be disabled by governance, for example during an incident. Check the Introduction to Boost section for more information about the Boost feature.

Example

console.log(await swapSDK.checkBoostEnabled());
// console output: true;
Last updated on