Cover your users' transaction fees from a domain treasury — the four conditions that must hold, and how to set them up.
Fee Sponsorship
What Fee Sponsorship Is
Normally each wallet pays its own fee (in the fee token) for every operation. A domain owner can instead sponsor those fees: the fee is pulled from the domain's treasury account, and the end-user's wallet pays nothing.
On-chain, a sponsored fee is the Domains contract executing transferFrom(treasury → feeWithdraw) on the fee token. That single fact drives everything below.
The Four Conditions
A sponsored transaction only succeeds when every one of these is configured. Each missing piece reverts with a different error:
| # | Condition | Error when missing |
|---|---|---|
| 1 | paysFees — the domain is set to sponsor. Off → users pay their own fee. | — (not an error; users just pay) |
| 2 | treasury — a treasury address is set and holds the fee token. | TreasuryNotSet |
| 3 | coin limit — a per-coin spending budget for the fee token. | CoinLimitExceeded |
| 4 | allowance — the treasury approved the Domains contract to spend its fee token. | ERC20InsufficientAllowance |
Condition 3 — The Spending Budget
setCoinLimit(origin, feeCoin, budget) caps how much the domain can spend on sponsored fees, per coin. When the accumulated spend plus the next fee would exceed the budget, the transaction reverts CoinLimitExceeded.
Amounts are wei (smallest unit) on the API and contract. The Passkallet dashboard converts from decimal for you.
Condition 4 — The Allowance
The treasury holds the fee tokens, but holding is not authorizing: the Domains contract can only transferFrom the treasury after the treasury has called approve(domainsContract, amount) on the fee token.
(spender = Domains contract, allowance = 0, needed = fee).One generous approve covers all future sponsored transactions until the allowance runs down.
Easiest Path: the Dashboard's Two Steps
The domain screen in the Passkallet dashboard sets up conditions 3 and 4 as a guided two-step card, always on the configured fee coin (no token picker):
| Step | What it does | Notes |
|---|---|---|
| 1 · Spending budget | Sets the fee-coin budget (setCoinLimit) | Shows spent vs. limit; decimal amounts |
| 2 · Allow the cost to be taken | The treasury approves the Domains contract | Shows the current allowance; signed by the treasury wallet's passkey — sign in as the treasury owner |
Both steps convert decimal amounts to wei automatically.
Doing It via the API
If the treasury is one of your org's Passkallet wallets, you can prepare the approve through the BaaS API instead:
curl -X POST "https://gateway.dev.passkallet.com/sepolia/api/v1/passkallet/baas/tokens/erc20/approve" \
-H "x-api-key: pk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4" \
-H "Content-Type: application/json" \
-d '{
"walletAddress": "<treasury wallet address>",
"tokenAddress": "<fee token address>",
"spender": "<Domains contract address>",
"amount": "1000000000000000000000"
}'The amount is wei — the example is 1000 tokens at 18 decimals. Sign the returned object with the treasury's passkey and submit via POST /baas/transactions/execute as usual.
Required scope: token:approve
Symptom Guide
Sponsored transactions failing one-by-one as you fix things is normal — the on-chain checks run in the order of the table above, so each fix surfaces the next missing condition until all four hold:
TreasuryNotSet → set the treasury
CoinLimitExceeded → raise the fee-coin budget
ERC20InsufficientAllowance → authorize collection (allowance)
✓ sponsored txs succeed