Cube

What Are Token Approvals?

Learn what token approvals are, how ERC-20 allowances and NFT approvals work, why they exist, and the main security risks behind them.

What Are Token Approvals? hero image

Introduction

Token approvals are the permissions that let one address spend or transfer tokens on behalf of another. They exist because many token interactions are not a simple “send from me to you” operation. A decentralized exchange, lending market, NFT marketplace, vault, bridge, or payment contract often needs the right to move assets after you agree to use it. Without approvals, those applications would have no standard way to pull tokens from your wallet when a transaction needs them.

That design solves a real coordination problem. But it also creates a second asset surface alongside your token balances: not just what you own, but who you have authorized to move it. In practice, many of the most expensive wallet-drain incidents do not begin with a private key theft. They begin with a valid approval that later turns out to be too broad, too long-lived, or granted to a vulnerable contract.

The key idea is simple: an approval separates ownership from spending authority. You still own the token, but you have delegated a limited power over it. Once that clicks, the rest of the topic becomes easier to reason about: why approvals exist, why ERC-20 uses allowances, why NFTs use a different model, why signed approvals like permit improve UX, and why “infinite approvals” are so dangerous.

Why do simple token transfers break many smart-contract workflows?

If tokens only supported direct transfers, then every movement of tokens would have to be initiated by the current holder. That works for ordinary payments, but it breaks the basic mechanics of many applications. A DEX contract cannot complete a swap by itself unless it can move the input tokens out of your wallet. A lending protocol cannot accept collateral unless it can pull the collateral tokens you promised to deposit. A marketplace cannot settle a sale if it has no permission to transfer the asset from seller to buyer.

This is the problem approvals solve. They create a two-step workflow. First, the token holder grants authority. Then, later, the authorized party exercises that authority under the token standard’s rules. In ERC-20, that workflow is expressed through approve, allowance, and transferFrom. In ERC-721, where each token is unique, the workflow is expressed through either a per-token approval or an operator approval covering all of an owner’s NFTs.

That distinction matters because the underlying assets are different. A fungible token is a quantity: 10 units out of 1,000. So the natural permission is “this spender may move up to x units.” An NFT is an object with identity: token #4821. So the natural permission is either “this address may move this specific NFT” or “this operator may manage all my NFTs.” The approval model follows the asset model.

How does ERC-20 allowance (approve/allowance/transferFrom) enable delegated spending?

For fungible tokens, the canonical approval mechanism comes from ERC-20. The owner calls approve(spender, value), which sets how many tokens spender may withdraw from the owner’s account. The token records that amount in an allowance mapping. Anyone can then query the remaining authorization through allowance(owner, spender). When the approved spender later calls transferFrom(from, to, value), the token checks whether the caller has enough allowance from from, and if so, transfers the tokens and reduces the allowance unless the implementation treats the approval as effectively infinite.

The most important thing to understand is that this is not a promise by the spender. It is a standing authorization recorded by the token contract. If you approve a contract for 100 tokens, the token contract itself will enforce that permission later when transferFrom is called. The spender does not need to ask you again. That is what makes DeFi composable: once the approval exists, downstream contract logic can rely on it.

The ERC-20 specification defines the core semantics very plainly. approve lets a spender withdraw up to a stated amount, and calling approve again overwrites the previous allowance. allowance returns how much remains. transferFrom is the “withdraw workflow” that allows authorized spending on someone else’s behalf. A successful approve must emit an Approval event, which is how wallets, block explorers, revocation tools, and indexing systems track permission changes.

A concrete example makes the flow clearer. Suppose Alice wants to deposit 500 USDC into a lending protocol. If she simply calls USDC’s transfer to the lending contract, the protocol may receive tokens, but ERC-20 does not require the receiving contract to be notified in a way that lets it safely book the deposit. The standard deposit pattern is different: Alice first calls the USDC token contract’s approve, naming the lending contract as spender for 500 USDC. Then she calls the lending protocol’s deposit function. Inside deposit, the lending contract calls USDC’s transferFrom(Alice, LendingPool, 500). The token contract sees that Alice granted the pool enough allowance, moves the tokens, and the lending protocol can continue knowing the transfer succeeded as part of its own transaction flow.

This is why approve plus transferFrom is so common. It gives the application pull-based settlement. The app can move exactly the amount it needs at the moment its own logic runs, rather than hoping the user separately sent tokens in the right order and amount.

What are the security risks of token approvals even when they behave correctly?

The same mechanism that makes approvals useful also makes them dangerous. Once a spender has authorization, the token contract will honor it according to the rules on-chain. If the spender contract is buggy, compromised, upgradeable in an unsafe way, or later manipulated through some other vulnerability, that existing approval becomes a live path to your wallet’s tokens.

This is the core security consequence: an approval is not just a convenience flag in a wallet interface. It is a durable capability. If it remains outstanding, it remains usable.

Research on approval-based exploits has shown why this matters at scale. The general failure mode is not that the token standard itself is broken, but that some approved contract exposes a path where an attacker can control the parameters of a transferFrom call. If many users had previously approved that contract, the bug can become a mass-drain vector. In other words, approvals amplify downstream contract risk. They turn “this app has a bug” into “this app has a bug and already has permission to pull user funds.”

This is also why the phrase infinite approval matters so much. In common implementations, setting allowance to the maximum uint256 value is treated as effectively unlimited. Some implementations do not decrement allowance when it is set to that maximum, making it function as a permanent authorization until explicitly revoked. From a UX perspective, users like this because they do not need to approve every transaction. From a security perspective, it means a future exploit can reach all eligible tokens, not just the amount needed for one action.

The convenience is real. So is the tradeoff.

What is the ERC-20 allowance race condition and how does it get exploited?

MethodRace riskTx countUX costWhen to use
Overwrite directlyHigh1 txLowNot recommended
Zero, then setLow (two-step)2 txsHigherSafer for reductions
Increase/decrease helpersLower (relative change)1 txModeratePrefer for incremental updates
Figure 112.1: Change ERC-20 allowance safely

There is a less obvious risk baked into classic ERC-20 approvals: changing an existing nonzero allowance can create a transaction-ordering problem. The specification explicitly notes this issue. If an owner tries to change a spender’s allowance from one nonzero value to another nonzero value, the spender may be able to use the old allowance before the new approval is processed, and then still benefit from the new allowance after it lands.

The mechanism is simple once you see it. Imagine Alice has approved Bob to spend 100 tokens. She now wants to reduce that to 50, so she sends approve(Bob, 50). But before that transaction is mined, Bob sees it in the mempool and uses transferFrom to spend the old 100. Then Alice’s new approval goes through, overwriting the allowance to 50. Bob has now spent 100 and still has permission for 50 more. The issue is not that the token miscomputed anything. The problem is that “replace old value with new value” is not atomic with respect to the spender’s use of the old value.

The ERC-20 spec does not require contracts to solve this at the token level, largely for backward compatibility. Instead, it recommends a client-side mitigation: set the allowance to 0 first, then set the new desired value in a separate step. That is why many wallets and applications encourage “reset approval” before changing it.

In practice, many libraries and implementations also introduced helper patterns like increasing or decreasing allowance relative to the current value, rather than overwriting from one arbitrary nonzero number to another. Those patterns are not part of the original ERC-20 standard itself, but they exist because the underlying problem is real.

Why do real-world ERC-20 integrations need defensive handling?

On paper, ERC-20 looks simple. In practice, integrations have to be defensive. The standard says callers must handle boolean failure returns and must not assume token methods always succeed. That sounds mundane, but it matters because not every token behaves consistently in the wild. Some return false on failure. Some revert. Some are only partly standard-compliant. Safe integration libraries exist largely because this edge of the ecosystem is rougher than the basic interface implies.

That implementation messiness affects approvals directly. A dApp or contract interacting with tokens cannot merely assume that approve or transferFrom behaved as expected. It must handle failures correctly. This is one reason wrapper libraries such as SafeERC20 are widely used: they normalize interaction patterns with tokens that may not strictly follow expected return conventions.

There is also a broader design limitation around ERC-20 token reception. The standard transfer function does not notify recipient contracts in a required way. Tokens sent directly to contracts that are not designed to manage them can become stuck. That is one reason the approve plus transferFrom deposit pattern became the norm for contracts. It lets the application coordinate token movement with its own bookkeeping in one transaction, instead of passively receiving tokens with no guaranteed callback discipline.

How does ERC-2612 permit change approvals and what new risks does it add?

MethodHow createdWho submitsGas to ownerReplay protectionMain risk
On-chain approveOwner sends approve transactionOwner (msg.sender)Owner pays gasTransaction ordering onlyAllowance race; persistent authority
ERC-2612 permitOff-chain EIP-712 signatureAny relayer can submitOwner pays no gasNonce + DOMAINSEPARATORRelayer censorship or front-run
Figure 112.2: Approve vs Permit (ERC-20)

Classic approve has an annoying UX property: the token owner must send an on-chain transaction as msg.sender just to grant permission. That means paying gas for the approval before the actual action that uses it. ERC-2612, usually called permit, extends ERC-20 so the owner can authorize an allowance with an off-chain signature instead.

Mechanically, permit does not remove allowances. It changes how they are created. Instead of the owner directly calling approve, the owner signs a typed message authorizing spender to have allowance value until some deadline. Anyone can submit that signed message on-chain by calling permit(owner, spender, value, deadline, v, r, s). If the signature is valid, the nonce matches, the deadline has not passed, and other checks hold, the token updates allowance[owner][spender], increments the owner’s nonce, and emits Approval.

The two key safeguards are the domain separator and the nonce. The domain separator binds the signature to a specific contract and chain, which helps prevent replay in the wrong context. The nonce ensures each signed permit can be used only once. Without a nonce, a relayer could keep resubmitting the same signed approval. Without domain separation, the same signature might become valid somewhere it was never intended to be used.

A worked example helps. Suppose Alice wants to trade a token on a DEX but holds no ETH for gas. With permit, her wallet can produce a signed message saying, in effect, “I authorize this DEX router to spend up to 1,000 units of this token before this deadline, using my current nonce.” A relayer or the DEX itself submits that permit on-chain, then follows with the swap transaction that uses transferFrom. Alice did not need to send a separate approval transaction herself. The approval still ends up on-chain, but its authorization came from a signature rather than from msg.sender.

That improves UX, but it does not remove risk. A signed permit is still an approval. It expresses allowance, not some richer notion of spending intent. The standard also notes a liveness issue: a relayer who receives a valid permit can choose not to submit it. Short deadlines help limit that free option. And because permit submission is not tied to msg.sender, another party can front-run and submit the signed approval first. None of that breaks the standard; it just means signed approvals inherit many of the same trust and timing considerations as normal approvals, plus a few new ones.

How do NFT approvals differ from ERC-20 allowances?

With ERC-721 NFTs, there is no fungible quantity allowance. Each token is unique, so the standard uses two distinct approval scopes instead. A holder can call approve(approved, tokenId) to authorize an address for a single NFT, or setApprovalForAll(operator, approved) to authorize or revoke an operator for all NFTs owned by that holder.

This is a cleaner fit for the asset model. You do not ask, “How many units of this NFT collection may be spent?” You ask either, “Who may transfer token #4821?” or “Which operator may manage my collection holdings?” The standard exposes corresponding query functions, getApproved(tokenId) and isApprovedForAll(owner, operator), so wallets and marketplaces can determine whether a transfer is authorized.

There is an important safety property here: the approved address for a single NFT is reset to none when that NFT is transferred. That automatic clearing prevents a stale per-token approval from surviving a sale and accidentally giving the old approved party power over the asset in its new owner’s hands. ERC-721 explicitly documents this behavior.

This is one place where NFTs avoid a major ERC-20 problem. The ERC-721 spec itself notes that there is no allowance because every NFT is unique, and therefore the standard avoids the allowance pattern and the issues later discovered around mutable fungible allowances. That does not make NFT approvals harmless (setApprovalForAll can still be extremely powerful and dangerous) but the structure of the risk is different.

How do other blockchains implement token approval or delegation?

The approval pattern is not unique to Ethereum, even though ERC-20 made it famous. On Solana, the SPL Token program uses an approve delegate model: a token account owner can authorize a delegate to transfer up to a maximum amount from that account. The underlying idea is the same as ERC-20 allowance: separate ownership from limited spending authority so another actor can move tokens later under defined rules.

What changes across architectures is the exact object being delegated. In Ethereum-style account-based tokens, approval is typically attached to an owner-spender pair inside a token contract. In Solana’s token model, delegation is tied to token accounts and program instructions. On Cardano, by contrast, the common wallet permission model standardized in CIP-30 is not an on-chain token allowance primitive at all; it is a dApp-to-wallet access model where a site requests permission to connect and later asks for explicit consent to sign transactions. That is still a permission system, but it lives at a different layer. It governs wallet access and signing, not a standing token-level transferFrom capability embedded in the asset standard.

This comparison helps separate two ideas people often blur together. A wallet permission says, “this application may ask the wallet for certain capabilities.” A token approval says, “this address may move these tokens under the token contract’s own rules.” They can coexist, but they are not the same thing.

How do approvals interact with key management and institutional controls?

At a deeper level, token approvals are an access-control mechanism. They answer the question: *who may cause this asset to move, besides its owner? * That makes them part of the same broader design space as multisig controls, delegated roles, and wallet permissions.

In institutional or exchange environments, the token-level approval is only one layer. The authority to authorize settlement may itself be protected by stronger key management. A useful real-world example is threshold signing. Cube Exchange uses a 2-of-3 threshold signature scheme for decentralized settlement: the user, Cube Exchange, and an independent Guardian Network each hold one key share; no full private key is ever assembled in one place, and any two shares are required to authorize a settlement. That is a different mechanism from ERC-20 approvals, but it solves a related problem: how to allow delegated or collaborative asset movement without concentrating unilateral power in a single key.

The connection is conceptual. Approvals define what a spender is allowed to do at the token layer. Threshold signing defines how authority over signing is distributed at the key layer. Both are about reducing friction while trying to control risk, but they operate in different places in the stack.

What are the common misunderstandings about token approvals?

The most common misunderstanding is to treat approvals as if they are part of a single transaction’s intent. Usually they are not. An approval is often a separate, reusable capability that persists until it is consumed, reduced, overwritten, or revoked. If you approve a protocol today and stop using it next month, that approval may still exist.

A second misunderstanding is to think “approved” means “safe because I approved it.” Approval only means the token contract will honor the spender’s requests within the allowed scope. It says nothing about whether the spender contract is well-designed, immutable, upgradeable, compromised, or exploitable.

A third misunderstanding is more technical: people often assume the spender is the only party who can submit an approval-related action. That is false for permit, where any address can submit a valid signed approval. This is by design. The authorization comes from the signature, not from who sends the transaction.

Developers have their own recurring misunderstanding: assuming token interactions are uniform. They are not. The ERC-20 standard itself warns that callers must handle false returns. Real tokens vary. Approval UX and safe contract integrations depend heavily on defensive engineering, not just on reading the nominal interface.

How should users and developers manage approvals in practice?

ActorActionScopeWhen to applyTools
End userApprove minimal amountLeast privilegeBefore granting permissionsRevocation UIs, approval scanner
Developer / dAppMinimize standing permissionsLimit spender scopeDesign flows to avoid infinite approvalsSafeERC20, try/catch, audits
Wallets & toolingHighlight and revoke approvalsSurface infinite approvalsOn approval dialogs and dashboardsIndexer + one-click revoke
Figure 112.3: Token approval best practices

For users, the practical lesson is straightforward: approve the smallest scope that still lets you do the thing you want, and treat stale approvals as live risk. Large or infinite approvals are not free convenience; they are persistent delegated authority.

For developers, the practical lesson is to design flows that minimize unnecessary standing permissions. If changing allowances, avoid unsafe overwrite patterns. If supporting permit, remember that it only grants allowance and may be submitted by anyone. If integrating ERC-20s generally, use defensive wrappers and failure handling rather than assuming all tokens are well-behaved. And if your contract will ever be approved as a spender, recognize that this elevates the consequences of any downstream bug in your own call paths.

The ecosystem is still experimenting with better approval semantics. Extensions such as spender-driven allowance reduction and alternative token standards try to smooth some of the old edges. But the core tradeoff remains the same: crypto applications need delegation to be usable, and every delegated power becomes part of the security perimeter.

Conclusion

**Token approvals are delegated spending rights. ** They exist because smart-contract applications need a standard way to move assets on a user’s behalf, not just receive direct transfers. In ERC-20, that delegation takes the form of allowances consumed through transferFrom; in ERC-721, it takes the form of per-token approvals and operator approvals.

That mechanism is powerful because it makes decentralized applications workable. It is risky for the same reason. Once you see approvals as persistent access control rather than as a one-time popup in a wallet, the security picture becomes much clearer: every approval is a capability, every capability needs a scope and a lifetime, and convenience usually comes from granting more power than the immediate action strictly requires.

How do you evaluate a token before using or buying it?

Evaluate a token by checking its design, on-chain controls, and market mechanics before you trade or grant approvals. Use Cube Exchange to run the practical steps: fund your account, inspect the token’s market and contract details, then execute a trade or approval within Cube’s order and transfer flow.

Frequently Asked Questions

Mechanically, how do ERC-20 approvals (approve/allowance/transferFrom) actually work and why are they used?
+
In ERC-20, an owner calls approve(spender, value) which records an allowance in the token contract; once set, any caller who meets the spender check can call transferFrom(from, to, value) and the token contract will move the tokens and (normally) reduce the allowance. This two-step pattern enables pull-based flows (e.g., deposits) so a contract can move tokens as part of its own transaction logic rather than relying on separate direct transfers.
Why are 'infinite approvals' considered dangerous if they just save me repeated gas costs?
+
An “infinite” approval typically uses type(uint256).max so the token contract treats the allowance as permanent and may not decrement it, which means a future exploit or bug in the approved contract could access all eligible tokens until you explicitly revoke the approval. That convenience avoids repeated approvals but creates a persistent delegated capability that many high-value incidents have exploited.
What is the ERC-20 allowance race condition and how should it be mitigated?
+
Changing a nonzero allowance directly can be front-run: an attacker can use the old allowance seen in the mempool before your change is mined, and then still benefit from the new allowance after it lands. The commonly recommended mitigation is a two-step reset: set the allowance to 0 first, wait for that to confirm, then set the new desired value (or use increase/decreaseAllowance helpers where available).
How does ERC-2612 'permit' change UX and what new risks does it introduce?
+
ERC-2612 permit lets an owner issue an off-chain signed approval that anyone can submit on-chain; this removes the owner’s need to pay gas for the approve transaction but introduces new timing and censorship considerations — a relayer can withhold submission and the signed permit can be frontrun by a third party. The permit still produces an on-chain allowance and therefore inherits the same delegated-risk profile as normal approvals.
Are NFT approvals safer than ERC-20 allowances because NFTs are unique?
+
For ERC-721, approvals are per-token (approve(tokenId)) or an operator-for-all (setApprovalForAll), and importantly a single-token approval is automatically cleared when that NFT is transferred, preventing a stale per-token approval from surviving a sale. Operator approvals remain powerful and must be treated cautiously, but the per-token clearing avoids a large class of stale-allowance problems that ERC-20 faces.
What’s the difference between a token approval and a wallet permission?
+
An approval is a token-level capability enforced by the token contract, which is different from a wallet permission model where a dApp asks a wallet to sign transactions; wallet permissions govern what a dApp can request from your wallet, while token approvals let another address or contract move tokens under the token contract’s rules. They can coexist, but they operate at different layers and have different threat models.
Can a spender or the token itself unilaterally revoke or nullify an allowance?
+
Yes — some token designs and specs allow a spender to unilaterally nullify or otherwise change an allowance (for example EIP-7410 documents unilateral nullification behavior), which can break assumptions for recurring payments or integrations that expect stable allowances. This behavior is part of why integrators must design defensively and why the ecosystem debates standardized mitigation patterns.
How should dApp developers safely integrate token approvals given nonstandard token behavior?
+
Because many tokens deviate from the ERC-20 reference (some return false, some revert, some implement slight variations), integrations should use defensive wrappers like SafeERC20, handle boolean/failure returns, and avoid assuming uniform behavior; these defensive patterns are widely recommended to avoid misinterpreting a failed approve/transferFrom.
Are token approvals unique to Ethereum or do other blockchains implement similar patterns?
+
The approval pattern exists across chains but is expressed differently: Solana’s SPL Token uses an approve-delegate tied to token accounts, Cardano’s CIP-30 is a wallet-level permission model rather than an on-chain allowance primitive, and Cosmos/Substrate ecosystems expose other module-level authorization patterns — the underlying idea of delegating spending authority is common, but the exact object and layer of delegation vary by chain.

Your Trades, Your Crypto