UNPKG

@renec-foundation/swap-router-sdk

Version:

TypeScript SDK for finding the optimal swap route

147 lines (116 loc) 4.79 kB
# Swap Router SDK This is a private package, so make sure to login to `npm` first. ``` yarn add @renec-foundation/swap-router-sdk ``` ## Compute the optimal swap route ```ts import Decimal from 'decimal.js' import { AnchorProvider } from '@project-serum/anchor' import { buildWhirlpoolClient, WhirlpoolContext } from '@renec-foundation/nemoswap-sdk' import { findOptimalRoute, PoolList, SwapOption } from '@renec-foundation/swap-router-sdk' import { useAnchorWallet } from '@renec-foundation/wallet-adapter-react' import { Commitment, Connection, PublicKey } from '@solana/web3.js' const commitment: Commitment = 'confirmed' const connection = new Connection(RPC_URL, { commitment }) const wallet = useAnchorWallet() const provider = new AnchorProvider(connection, wallet, { commitment }) const context = WhirlpoolContext.withProvider(provider, new PublicKey(POOL_PROGRAM_ID)) const client = buildWhirlpoolClient(context) // List of pools to search on. This can be fetched from backend. declare const poolList: PoolList[] // Token pair for swapping. declare const tokenAMintAddress: PublicKey declare const tokenBMintAddress: PublicKey // The amount of token for swapping. // This can be the amount of tokenA or tokenB, depending on the swap option. declare const tokenAmount: Decimal // If ExactIn, router will find the route that gives the most tokenB for a given amount of tokenA. // If ExactOut, router will find the route that needs the least tokenA for a given amount of tokenB. declare const option: SwapOption const route = await findOptimalRoute({ client, poolList, tokenAMintAddress, tokenBMintAddress, tokenAmount, option, }) if (route) { const txBuilder = await getSwapTxBuilderFromRoute(route) await txBuilder.buildAndExecute() } ``` ## Error handling To get an exception if no route is found, set `silent` to `false`. ```ts import { SwapError } from '@renec-foundation/swap-router-sdk' try { const route = await findOptimalRoute({ client, poolList, tokenAMintAddress, tokenBMintAddress, tokenAmount, option, silent: false, }) } catch (err) { if (err instanceof SwapError) { const { code, message } = err } } ``` ## Versioned transaction and lookup table ```ts import { getV0TxFromRoute } from '@renec-foundation/swap-router-sdk' // Addresses of lookup tables that should be used for this swap. declare const lookupTableAddresses: PublicKey[] const v0Tx = await getV0TxFromRoute(client, route, lookupTableAddresses) await connection.sendTransaction(v0Tx) ``` ## Serialize and deserialize You may want to serialize the route info before sending as JSON. ```ts import { deserializeRoute, serializeRoute } from '@renec-foundation/swap-router-sdk' // Send route info as JSON. const serializedRoute = serializeRoute(route) const data = JSON.stringify(serializedRoute) // Receive JSON route info and deserialize it. const parsedData = JSON.parse(data) const deserializedRoute = await deserializeRoute(client, parsedData) if (deserializedRoute) { const txBuilder = await getSwapTxBuilderFromRoute(deserializedRoute) await txBuilder.buildAndExecute() } ``` ## Average price To calculate the average price of the route, we first weight the *accumulated* [square-root price](https://orca-so.gitbook.io/orca-developer-portal/whirlpools/architecture-overview/price-and-ticks) of all pools in each sub-route by its contribution to the overall route, i.e., `splitRate`, and then compute the total of all weighted prices. ```ts import { computeAccumulatedSqrtPriceOfPoolRoute, convertSwapRouteToPoolRoute, } from '@renec-foundation/swap-router-sdk' let sqrtPriceOfRoute = new Decimal(0) route.subRoutes.forEach((subRoute) => { const poolRoute = convertSwapRouteToPoolRoute(subRoute.quote.route) const sqrtPriceOfPoolRoute = computeAccumulatedSqrtPriceOfPoolRoute(poolRoute) // Weight each sqrtPrice by its contribution to the overall route, i.e., splitRate. sqrtPriceOfRoute = sqrtPriceOfRoute.add(sqrtPriceOfPoolRoute.price.mul(subRoute.splitRate)) }) // Note that this price is NOT equivalent to the rate // between the amount of token received over spent. const actualRateBasedOnQuote = route.estimatedAmountOut.div(route.estimatedAmountIn) console.log(sqrtPriceOfRoute.eq(actualRateBasedOnQuote)) // false ``` ## Swap fee For each swap instruction (a.k.a. *hop*) from token A to B, a swap fee, which is a small amount of **token A**, will be incurred. To calculate the total fee of the entire transaction, we need to accumulate the fee of all hops. ```ts import { getSwapFeeOfHop, SwapFee } from '@renec-foundation/swap-router-sdk' const fees: SwapFee[] = [] route.subRoutes.forEach((subRoute) => { subRoute.quote.route.forEach((hop) => { fees.push(getSwapFeeOfHop(hop)) }) }) ```