Refund Liquidity (v2)
Refund liquidity on STON.fi v2 - handle failed operations with vault-based architecture
Refund tokens that were sent to an LP account but were not added to the pool.
The production-ready approach is API-driven. Use the STON.fi API to resolve router metadata, construct contracts via
dexFactory, and let the SDK derive the correct on-chain calls. This avoids hardcoding addresses and remains forward-compatible with router updates.
Mainnet workflow
Determine the router and LP account that needs the refund. You can fetch them from the simulation result, from
getPoolsByAssetPair, or fromgetWalletLpAccountson the STON.fi API.Fetch the router metadata with
getRouter(routerAddress)and build the contract instances dynamically.Call
getRefundTxParamson the LP account to obtain the message you need to send on-chain.
import { Client, dexFactory } from "@ston-fi/sdk";
import { StonApiClient } from "@ston-fi/api";
const tonClient = new Client({
endpoint: "https://toncenter.com/api/v2/jsonRPC",
apiKey: process.env.TON_API_KEY,
});
const apiClient = new StonApiClient();
const routerMetadata = await apiClient.getRouter("<router address>");
const dexContracts = dexFactory(routerMetadata);
const lpAccount = tonClient.open(
dexContracts.LpAccount.create("<lp account address>"),
);
// Optional: inspect the pending balances before refunding
const lpAccountData = await lpAccount.getLpAccountData();
console.log({
routerAddress: lpAccountData.routerAddress?.toString(),
poolAddress: lpAccountData.poolAddress?.toString(),
tokenABalance: lpAccountData.amount0.toString(),
tokenBBalance: lpAccountData.amount1.toString(),
});
const refundTxParams = await lpAccount.getRefundTxParams({
queryId: 12345,
});Send the resulting parameters using your preferred wallet integration (see the transaction sending guide).
Deriving the LP account address
If you only know the assets and the user wallet:
const [poolInfo] = await apiClient.getPoolsByAssetPair({
asset0Address: "<token A address or 'ton'>",
asset1Address: "<token B address>",
});
if (!poolInfo) {
throw new Error("Liquidity pool not found for the provided asset pair");
}
const routerMetadata = await apiClient.getRouter(poolInfo.routerAddress);
const dexContracts = dexFactory(routerMetadata);
const pool = tonClient.open(
dexContracts.Pool.create(poolInfo.address),
);
const lpAccountAddress = (
await pool.getLpAccountAddress({ ownerAddress: "<your wallet address>" })
).toString();You can then pass lpAccountAddress into the main workflow above.
TON pools: When a pool includes TON, the router metadata exposes
ptonMasterAddress.dexFactoryautomatically wires the correct pool/LP implementations—no manual proxy setup is required.
Testnet refund (manual setup)
Only rely on testnet for experimentation. Because api.ston.fi is mainnet-only, you must hardcode contract addresses, mint or source the testnet jettons (TesREED/TestBlue), and create funded pools before you can refund LP accounts.
import { TonClient } from "@ton/ton";
import { DEX } from "@ston-fi/sdk";
const client = new TonClient({
endpoint: "https://testnet.toncenter.com/api/v2/jsonRPC",
});
const router = client.open(
DEX.v2_1.Router.CPI.create("kQALh-JBBIKK7gr0o4AVf9JZnEsFndqO0qTCyT-D-yBsWk0v"), // CPI Router v2.1.0 (testnet)
);
const pool = client.open(
await router.getPool({
token0: "kQDLvsZol3juZyOAVG8tWsJntOxeEZWEaWCbbSjYakQpuYN5", // TesREED jetton (testnet)
token1: "kQB_TOJSB7q3-Jm1O8s0jKFtqLElZDPjATs5uJGsujcjznq3", // TestBlue jetton (testnet)
}),
);
const lpAccount = client.open(
await pool.getLpAccount({ ownerAddress: "<your testnet wallet>" }),
);
const txParams = await lpAccount.getRefundTxParams({
queryId: 12345,
});This manual approach is strictly for testing. Switch back to the API-driven workflow for any mainnet-facing integration.
Last updated