Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Troubleshooting — Approve · Permit · Permit2 · Boson Protocol
Skip to content

Approve · Permit · Permit2

Symptom

You call commitToOffer on an ERC-20-priced offer and the tx reverts with InsufficientAllowance, ERC20: insufficient allowance, or a similar transfer error.

Causes

1. Spender mismatch

You approved the wrong contract. The spender must be the Diamond, not the voucher (rNFT) contract.

// WRONG
await token.approve(voucherContract, amount)
 
// RIGHT
await token.approve(diamondAddress, amount)
 
// EVEN BETTER (handled by the SDK)
await sdk.approveExchangeToken(offerId, amount)

2. Amount too small

You approved price but didn't account for protocol/agent fees, or the offer has a buyer deposit.

For a typical offer:

  • Buyer pays offer.price.
  • Approval must be ≥ offer.price.

For offers with non-zero buyer deposit:

  • Buyer pays offer.price + offer.buyerCancelPenalty... no, wait: buyerCancelPenalty only matters on cancel. The amount transferred at commit is offer.price.

Approve a comfortable surplus (e.g. offer.price * 2) to avoid this.

3. EIP-2612 Permit vs. ERC-3009 confusion

USDC uses ERC-3009 (receiveWithAuthorization), not EIP-2612 Permit. If you sign a Permit and send it for USDC, it'll either revert or be silently ignored.

Use:

  • ERC3009 for USDC, EURC, and other ERC-3009-supporting tokens.
  • Permit (EIP-2612) for DAI, USDS, and others.
  • Permit2 as a universal fallback.

See Concepts → Signing → Token-auth meta-tx and x402 → Token-auth strategies.

4. Permit2 not pre-approved

Permit2 requires a one-time approval of the Permit2 contract itself for each token. Once that's done, per-use authorizations are signed envelopes (no on-chain tx). If your buyer hasn't pre-approved Permit2 for the token, the per-use authorization will fail.

const PERMIT2 = "0x000000000022D473030F116dDEE9F6B43aC78BA3"
await token.approve(PERMIT2, ethers.constants.MaxUint256)
// now Permit2-style signed envelopes work

5. ERC-20 race conditions

Some tokens (USDT, classic implementations) refuse to update allowance from a non-zero value. You must approve 0 first, then the new amount:

await token.approve(spender, 0).then(t => t.wait())
await token.approve(spender, amount).then(t => t.wait())

The SDK's approveExchangeToken handles this for known-problematic tokens.

Fix summary

For 95 % of cases:

await (await sdk.approveExchangeToken(offerId, /* amount */ offer.price)).wait()
await (await sdk.commitToOffer(buyer, offerId)).wait()

For gas-optimized UX:

const signed = await sdk.metaTx.signMetaTxWithTokenTransferAuthorization({
  functionName: "commitToOffer",
  functionArgs: { buyer, offerId },
  tokenAuth: { type: "ERC3009" }, // for USDC
})
await sdk.metaTx.relayForwardedMetaTransaction(signed)

Related