Skip to content
Approvals

LCX Liberty. American DeFi. Your keys. Your assets. Your control.

Core concepts

Approvals

Checking allowance, gasless permits, and the Permit2 fallback.

Check first

GET/v1/allowance?chainId=1&token=0x…&taker=0x…&amount=…

Returns whether this taker must approve before swapping, their current allowance, and the exact spender. The spender is read from the same place the calldata takes it from, so the two cannot disagree. In a hand-rolled integration they do disagree eventually, and the result is a transaction that reverts with nothing on screen to explain it.

If a chain has no router deployed, spender and required both come back null. Null is not “no approval needed”. It means there is nothing to approve because there is nothing to execute against there.

Native ETH needs no approval at all. A quote with nativeIn: true skips this step; the router wraps and unwraps.

Gasless, where the token allows it

POST/v1/permit

Returns EIP-2612 typed data built from the token's own name, version and nonce, read from the chain rather than assumed. Reading them matters more than it sounds. USDC's domain version is "2", and a signature built against a hardcoded "1" is rejected on-chain with nothing useful to put in front of the user.

ts
const typedData = await lcx.permit({ chainId: 1, token, owner });
const signature = await wallet.signTypedData(typedData);

When the token has no permit

Tokens that do not implement EIP-2612 return PERMIT_UNSUPPORTED, along with the Permit2 address so you can route through it if you already support that. Otherwise fall back to a plain approve against the spender from /v1/allowance.

ts
try {
  const typedData = await lcx.permit({ chainId: 1, token, owner });
  const signature = await wallet.signTypedData(typedData);
} catch (e) {
  if (e.code === "PERMIT_UNSUPPORTED") await erc20.approve(spender, amount);
  else throw e;
}