@renec-foundation/swap-router-sdk
Version:
TypeScript SDK for finding the optimal swap route
147 lines (116 loc) • 4.79 kB
Markdown
# 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))
})
})
```