Skip to content

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 commit 44c9e16 of 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

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:removeLiquidity

Source 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

Curated independently. Source-attributed reference for The Root Network. Not affiliated with Futureverse / TRN Labs.