DEX (Native API example)
Swap one token for another, and add or remove liquidity. Reference: dex pallet.
Copied from Futureverse’s
futureversecom/trn-examples, deleted with its GitHub organisation, at commit44c9e16of 2024-10-31. Apache-2.0: license. Before you run it, read Running the examples.
Offline destinations
Some places this page points to did not answer when last checked:
docs-beta.therootnetwork.com— does not resolve (checked 2026-10-18)stackblitz.com/github/futureversecom— StackBlitz loads these projects from the deleted futureversecom organisation on GitHub (checked 2026-10-18)
Run in StackBlitz (repository deleted, checked 2026-10-18) · Pallet Documentation (offline, checked 2026-10-18)
IMPORTANT
Ensure the following ENV vars are available before running the examples
CALLER_PRIVATE_KEY- Private key of an account that submits the transaction. Follow this guide to create and fund an account with some test tokens on Porcini (testnet) if you don't have one yet.
Examples
bash
# change your working directory to this example first
cd examples/substrate/use-dex
# export all required environments
export CALLER_PRIVATE_KEY=
# swap one token to another
pnpm call:swap
# add liquidity to liquidity pool
pnpm call:addLiquidity
# remove liquidity from liquidity pool
pnpm call:removeLiquiditySource files
src/addLiquidity.ts
ts
import { filterExtrinsicEvents } from "@trne/utils/filterExtrinsicEvents";
import { formatEventData } from "@trne/utils/formatEventData";
import { ASTO_ASSET_ID, XRP_ASSET_ID } from "@trne/utils/porcini-assets";
import { sendExtrinsic } from "@trne/utils/sendExtrinsic";
import { withChainApi } from "@trne/utils/withChainApi";
interface Liquidity {
0: number;
1: number;
}
/**
* Use `dex.addLiquidity` to add more liquidity for existing pair [XRP, ASTO], following the
* existing ratio between the 2 tokens
*
* Assumes caller has ASTO for liquidity and XRP for both gas and liquidity.
*/
withChainApi("porcini", async (api, caller, logger) => {
const tokenA = XRP_ASSET_ID;
const tokenB = ASTO_ASSET_ID;
// 💡 use `api.rpc.dex.getLiquidity` to get current ratio
const liquidity = (await api.rpc.dex.getLiquidity(tokenA, tokenB)) as unknown as Liquidity;
const tokenALiquidity = BigInt(liquidity["0"]);
const tokenBLiquidity = BigInt(liquidity["1"]);
const amountADesired = BigInt(1_000_000); // 1 XRP
const amountBDesired = (amountADesired * tokenBLiquidity) / tokenALiquidity;
const amountAMin = (amountADesired * BigInt(95)) / BigInt(100); // 5% slippage
const amountBMin = (amountBDesired * BigInt(95)) / BigInt(100); // 5% slippage
logger.info(
{
parameters: {
tokenA,
tokenB,
amountADesired,
amountBDesired,
amountAMin,
amountBMin,
},
},
`create a "dex.addLiquidity" extrinsic`
);
const extrinsic = api.tx.dex.addLiquidity(
tokenA,
tokenB,
amountADesired.toString(),
amountBDesired.toString(),
amountAMin.toString(),
amountBMin.toString(),
null, // to
null // deadline
);
logger.info(`dispatch extrinsic from caller="${caller.address}"`);
const { result, extrinsicId } = await sendExtrinsic(extrinsic, caller, { log: logger });
const [addEvent] = filterExtrinsicEvents(result.events, ["Dex.AddLiquidity"]);
logger.info(
{
result: {
extrinsicId,
blockNumber: result.blockNumber,
addEvent: formatEventData(addEvent.event),
},
},
"receive result"
);
});src/removeLiquidity.ts
ts
import { filterExtrinsicEvents } from "@trne/utils/filterExtrinsicEvents";
import { formatEventData } from "@trne/utils/formatEventData";
import { ASTO_ASSET_ID, XRP_ASSET_ID } from "@trne/utils/porcini-assets";
import { sendExtrinsic } from "@trne/utils/sendExtrinsic";
import { withChainApi } from "@trne/utils/withChainApi";
import assert from "node:assert";
interface LiquidityToken {
Ok: number;
}
/**
* Use `dex.removeLiquidity` to remove your own liqidity from the pair [XRP, ASTO]
*
* Assumes caller liquidity of the pair and XRP to pay gas.
*/
withChainApi("porcini", async (api, caller, logger) => {
const tokenA = XRP_ASSET_ID;
const tokenB = ASTO_ASSET_ID;
const lpAssetId = (await api.rpc.dex.getLPTokenID(tokenA, tokenB)) as unknown as LiquidityToken;
const liquidity = await api.query.assets.account(lpAssetId.Ok, caller.address);
assert(liquidity.isSome);
logger.info(
{
liquidityBalance: {
lpAssetId: lpAssetId.Ok,
liquidity,
},
},
`${caller.address} liquidity balance`
);
const amountAMin = 0;
const amountBMin = 0;
logger.info(
{
parameters: {
tokenA,
tokenB,
liquidity: liquidity.unwrap().balance.toString(),
amountAMin,
amountBMin,
},
},
`create a "futurepass.proxyExtrinsic" extrinsic`
);
const extrinsic = api.tx.dex.removeLiquidity(
tokenA,
tokenB,
liquidity.unwrap().balance,
amountAMin,
amountBMin,
null, // to
null // deadline
);
const { result, extrinsicId } = await sendExtrinsic(extrinsic, caller, { log: logger });
const [removeEvent] = filterExtrinsicEvents(result.events, ["Dex.RemoveLiquidity"]);
logger.info(
{
result: {
extrinsicId,
blockNumber: result.blockNumber,
removeEvent: formatEventData(removeEvent.event),
},
},
"receive result"
);
});src/swap.ts
ts
import { filterExtrinsicEvents } from "@trne/utils/filterExtrinsicEvents";
import { formatEventData } from "@trne/utils/formatEventData";
import { ASTO_ASSET_ID, XRP_ASSET_ID } from "@trne/utils/porcini-assets";
import { sendExtrinsic } from "@trne/utils/sendExtrinsic";
import { withChainApi } from "@trne/utils/withChainApi";
interface AmountsOut {
Ok: [number, number];
}
/**
* Use `dex.swapWithExactSupply` to swap XRP for ASTO, with exact amount in.
*
* Similar swap called can be used is `dex.swapWithExactTarget` to swap token with exact amount out.
*
* Assumes caller has XRP to pay for gas and to swap for ASTO.
*/
withChainApi("porcini", async (api, caller, logger) => {
const tokenA = XRP_ASSET_ID;
const tokenB = ASTO_ASSET_ID;
const oneXrp = 1_000_000;
// use `dex.getAmountsOut` to determin the minimum amount of ASTO from 1 XRP
const {
Ok: [amountIn, amountOut],
} = (await api.rpc.dex.getAmountsOut(oneXrp, [tokenA, tokenB])) as unknown as AmountsOut;
const amountOutMin = amountOut * 0.95; // 5% slippage
logger.info(
{
parameters: {
amountIn,
amountOutMin,
path: [tokenA, tokenB],
},
},
`create a "dex.swapWithExactSupply" extrinsic`
);
const extrinsic = api.tx.dex.swapWithExactSupply(
amountIn.toString(),
amountOutMin.toString(),
[tokenA, tokenB],
null, // to
null // deadline
);
logger.info(`dispatch extrinsic from caller="${caller.address}"`);
const { result, extrinsicId } = await sendExtrinsic(extrinsic, caller, { log: logger });
const [swapEvent] = filterExtrinsicEvents(result.events, ["Dex.Swap"]);
logger.info(
{
result: {
extrinsicId,
blockNumber: result.blockNumber,
swapEvent: formatEventData(swapEvent.event),
},
},
"receive result"
);
});package.json
json
{
"scripts": {
"call": "tsx --tsconfig ../../../tsconfig.json",
"call:swap": "pnpm call src/swap.ts",
"call:addLiquidity": "pnpm call src/addLiquidity.ts",
"call:removeLiquidity": "pnpm call src/removeLiquidity.ts"
}
}Source:
futureversecom/trn-examples, examples/substrate/use-dex (deleted) · commit 44c9e16, 2024-10-31 · Apache-2.0, license · part of Code examples