Crypto Swap Approvals: Why Your Swap Needs None
A custodial swap needs no wallet connection and no token approval. We counted 432 approve calls in 708 seconds - 25.7% unlimited - and explain what to revoke.
The form that asks for no signature. On 2026-11-09 I pulled veilswap.io/exchange and counted the strings: 46,702 bytes, zero occurrences of walletconnect, eth_requestAccounts, ethereum.request, MetaMask or "connect wallet". The order API takes seven fields, five required — pair, amount, payout, refund — and no signature. On Ethereum, the same hour told a different story. Between 09:57:59 and 10:09:47 UTC (blocks 26,376,584 to 26,376,643, 708 seconds) 17,849 transactions included 432 approve calls and 26 setApprovalForAll calls. One flow asks you to send; the other asks you to grant.
What approve actually writes. An ERC20 token keeps a mapping called allowance: `allowance[owner][spender]`. `approve(spender, value)` writes one entry into that mapping. It does not move a coin. It authorises the spender contract to call `transferFrom(owner, ...)` up to that number, later, without a fresh signature from you. A DEX router needs this: it pulls your tokens inside the swap transaction. A custodial swap does not: you send a normal transfer to a deposit address, the pool detects it, and pays out from its own float on the destination chain. Your wallet signs one ordinary send per leg, and nothing else.
The 708-second census. 432 direct approve calls work out to 0.61 per second - 2.4% of all transactions in the window; approvals wrapped inside a multicall do not expose this selector at the top level, so this is a lower bound. 111 set exactly 2^256-1, the maximum a uint256 can hold: 25.7% of approvals, one unlimited grant every 6.4 seconds. Another 53 wrote zero - a revocation, or the first half of a USDT allowance change - 12.3%, so revoking is routine. The calls touched 143 distinct token contracts: USDT 106, USDC 96, WETH 11, LINK 7, WBTC 7. Inside USDT, 12 of 106 were unlimited (11.3%) and the median positive approval was 500 USDT; inside USDC, 19 of 96 (19.8%) and the median 673.3. A 10-block sample of 78 approvals had 77 successful receipts (98.7%) and a median gas cost of 48,501, ranging 24,080-120,000. (XMR to USDT exit)
Why unlimited wins, and what it costs. An approval is not free: the sampled median was 48,501 gas. At the 0.09 gwei base fee I read at 10:10 UTC that is 0.00000437 ETH — about $0.0092 at ETH $2,104. At 30 gwei the same approval costs 0.001455 ETH or $3.06; at 100 gwei, $10.21. Approving the maximum once avoids repeating that cost for every future trade with the same token and spender. The trade: save one transaction of roughly three dollars at normal fees, and leave a contract the right to move your whole balance at any time. That is rational only if you trust the spender and your key hygiene; if either fails, the saving is irrelevant. (how Monero fees work)
Permit, Permit2, and the signature that is an approval. EIP-2612 moved approvals off-chain: a `permit` signature carries the amount, spender, nonce and deadline, and the token contract accepts it without an `approve` transaction. No gas, no on-chain approve row — and no wallet prompt that says "approve". Permit2 (0x000000000022D473030F116dDEE9F6B43aC78BA3) goes further: one on-chain approval to the Permit2 contract, then per-token signatures for each transfer. Both are legitimate tools, and both are what phishing kits ask for when they want an approval that does not look like one. `setApprovalForAll` has the same shape for NFTs: 26 calls in 708 seconds, each granting an operator every token in a collection. A custodial swap needs none of these — there is nothing to permit.
What can actually go wrong. An allowance by itself moves nothing; the spender has to call `transferFrom`. The risk is that the spender is malicious, or honest today and compromised tomorrow. Then it can move up to the allowance at any time, with no further signature, and you will not see a wallet prompt because no signature is needed. On USDT an unlimited approval means your whole USDT balance in that address. Rotating to a new seed does not revoke the old allowances: the old address keeps its approvals for as long as it holds the token, so "move to a fresh wallet" is only half a fix. Revoking is one transaction — 53 of 432 approvals in our window were exactly that — and for USDT the contract requires resetting to zero first when the current allowance is non-zero, so changing an existing USDT approval takes two transactions, not one. (XMR to USDT exit)
The swap side, measured. VeilSwap's order flow is the opposite: choose the pair, amount, payout and refund addresses, then send from your wallet. The only signature is the ordinary transfer, and it should be verified on the signing device, not on the laptop. The order page returns a deposit address and a `validUntil`; in earlier tests a 100 USDT order carried 601 seconds, while the quote that preceded it was valid for 120. The order API stores the payout and refund addresses server-side and never asks for a wallet connection — there is no dapp browser, no `eth_requestAccounts`, no permit. The only secret in the flow is the order UUID link: anyone holding it can see status, amounts and the deposit address, but not the payout or refund address,. If a page that calls itself a swap asks you to connect a wallet or sign a message before it will show a deposit address, it is not this flow. (recovering misdirected funds)
A seven-point allowance check. (1) Approve the amount you are about to trade, not the maximum, unless you trust that spender long term. (2) Read every `permit` and `Permit2` signature as an approval: check the spender, the amount and the deadline; a "sign in" prompt that contains an amount is an allowance. (3) Revoke after the trade — revoke.cash or Etherscan's token approval checker — and remember USDT's zero-first rule. (4) Use a separate hot wallet for trading; approving from a wallet that holds savings turns one bad contract into a total loss. (5) Treat `setApprovalForAll` as handing over the whole collection, not one NFT. (6) Check the spender address itself; a fake router with a similar name is the whole attack. (7) If you only need to swap, prefer a flow that never connects: no connection, no allowance, nothing to revoke. (XMR to USDT exit)
Boundary and the live snapshot. These are input counts, not a security audit: 432 direct `approve` calls in 60 blocks, with a 10-block receipt sample at 98.7% success. The point is scale, not attribution: approvals are routine, and roughly a quarter are unlimited. At 10:04 UTC on 2026-11-09 the site quoted 100 USDT-TRC20 at 0.14778107 XMR (676.68 USD per XMR implied), 500 at 0.829121 (603.05), 1,000 at 1.658805 (602.84) and 5,000 at 8.296278 (602.68); 1 XMR sold for 590.223 USDT, and the catalogue listed 448 assets on 65 networks, six needing an extra ID. Quotes move every minute and the page governs. The permission surface does not move: a swap needs a transfer, not an allowance. (XMR to USDT exit)