Pool Interface
Everything your calling contract needs, in one place: two functions to swap, eight to check before you swap, two more if you deliver output to a third party, and one to confirm the version.
If you need something that is not listed here, please raise it during onboarding and we can tell you whether it is available.
Check the version first. The pool is upgradeable. These pages describe version 7.
function getVersion() pure returns (string)
If it returns anything other than "7", please check with your Franklin Templeton contact whether
these pages still describe your pool. We let you know when a pool is upgraded.
Before you swap
Every one of these is a free read. Working through them costs nothing and catches most refusals before you spend gas — see Check eligibility.
function isTraderAllowed(address trader, address tokenA, address tokenB) view returns (bool)
function isTokenPairAuthorized(address tokenA, address tokenB) view returns (bool)
function paused() view returns (bool)
function getTokenBalance(address token) view returns (uint256)
function registeredTokens(address token) view returns (bool isRegistered, uint8 decimals)
function getTokenPairHash(address tokenA, address tokenB) pure returns (bytes32)
function minAmountInByPair(bytes32 pairHash) view returns (uint256)
function minAmountInByDirection(bytes32 directionHash) view returns (uint256)
isTraderAllowed takes the trader first. Passing the arguments in another order silently looks
up a token as the trader and returns false for everything.
getTokenBalance is the deliverable reserve — balanceOf on the pool is not. The reserve is not
necessarily held at the pool's own address, so balanceOf can return a figure unrelated to what the
pool can deliver, in either direction.
Read decimals from the pool, not from the token
registeredTokens returns the decimals the pool cached when the token was registered, and those are
the values it uses for every conversion. Read them here rather than calling decimals() on the token
contract.
const [isRegistered, decimals] = await client.readContract({
address: POOL, abi: poolAbi, functionName: 'registeredTokens', args: [IBENJI]
});
The two minimums, and the two different hashes
A pair can carry a minimum input amount, and a single direction of that pair can carry its own. Where a direction minimum is set it overrides the pair's. Check both.
The two hashes are not computed the same way. The pair hash comes from the pool and is order-independent; the direction hash you compute yourself, and the order matters.
const pairHash = await client.readContract({
address: POOL, abi: poolAbi, functionName: 'getTokenPairHash', args: [USDC, IBENJI]
});
const directionHash = keccak256(encodePacked(['address', 'address'], [USDC, IBENJI]));
const [pairMin, dirMin] = await Promise.all([
client.readContract({
address: POOL, abi: poolAbi,
functionName: 'minAmountInByPair', args: [pairHash]
}),
client.readContract({
address: POOL, abi: poolAbi,
functionName: 'minAmountInByDirection', args: [directionHash]
})
]);
const minimum = dirMin > 0n ? dirMin : pairMin;
A minimum of 0 means none is set.
Swapping
function quoteNet(address fromToken, address toToken, uint256 amount, address client)
view returns (uint256 net, uint256 fee)
function swap(address fromToken, address toToken, uint256 amount, address destination, uint256 minAmountOut)
Pass your calling contract's address as client when you quote — the fee is keyed on the input
token and the calling contract, so quoting as your own wallet can return a figure you will not be
charged. See Your contract is the trader.
amount, net, fee and minAmountOut are all in the smallest unit of the relevant token.
Name the full swap signature explicitly. The pool has three functions called swap, and the
two shorter forms take no minAmountOut — they place no minimum on what you receive. Always use
the five-argument form.
JavaScript libraries cannot tell which form a bare pool.swap(...) call means, and fail before the
call reaches the network. Dropping arguments until the error stops selects a form with no minimum —
do not resolve it that way. Name the signature instead:
const hash = await wallet.writeContract({
address: POOL,
abi: parseAbi([
'function swap(address fromToken, address toToken, uint256 amount, address destination, uint256 minAmountOut)'
]),
functionName: 'swap',
args: [USDC, IBENJI, amount, destination, net]
});
Solidity callers are unaffected — the compiler resolves overloads by argument type.
Only for third-party delivery
Skip this section if the output goes to your own calling contract.
function setDestinationOverrideApproval(
address fromToken,
address toToken,
uint256 amount,
address destination,
uint256 deadline
)
function getDestinationOverrideApproval(
address trader,
address fromToken,
address toToken,
address destination
) view returns (uint256 deadline, uint256 remainingAmount)
Your contract must grant this itself. The approval is keyed on the caller, so one granted from your owner wallet writes a record the swap never reads. See Destination override approval for the full lifecycle.
deadline is a block number, not a timestamp. It is compared against the current block number, so
pass a block number rather than a time:
- A Unix timestamp is accepted — it is simply a very large block number — and creates an approval that in practice never expires. Nothing reports this at the point you set it.
- A duration in seconds is rejected, because it is a block number already in the past.
Read the current block number and add to it:
const deadline = await client.getBlockNumber() + 500n;
trader is the first argument of getDestinationOverrideApproval, and the return is
(deadline, remainingAmount) — deadline first. Get either wrong and the call returns (0, 0),
which is indistinguishable from "no approval exists."
What is not here
There is no caller-accessible way to add to the pool's reserve or to take anything out of it. Deposit entry points appear in the ABI but are retired and revert if called, and the withdrawal path is restricted to the operator — so nothing here lets you recover tokens you send the pool directly. See Overview. Two things are not available to callers in this version: swaps executed on behalf of another wallet, and Permit2 — no swap entry point accepts a permit, so your input tokens arrive by ordinary transfer and an ERC-20 allowance. If you need either, please raise it during onboarding.