Asset-level spending limits and recovery: should rate limiting live in the asset rather than the account?

Placing spending limits and recovery mechanisms at the token contract (asset level) can close allowance and delegation gaps, but adds complexity and may not be practical for native ETH.

Asset-level spending limits and recovery: should rate limiting live in the asset rather than the account?

Asset‑Level Spending Limits vs. Account‑Level Controls

TL;DR – Moving rate‑limiting logic from a wallet’s code to the token contract (or a system contract for ETH) can stop attackers from bypassing limits via allowances or delegation, yet it introduces new architectural overhead, UX fragmentation, and scalability concerns.

---

Why Consider Asset‑Level Enforcement?

Current key‑compromise defenses—smart‑wallet modules, ERC‑7579, social‑recovery, and EIP‑7702 delegation—store policy in the account. This works while the account’s code is on the execution path, but two notable gaps appear:

  1. Allowance bypass – An approved `transferFrom` can move tokens without re‑entering the wallet’s validation logic, rendering a daily limit ineffective.
  2. Delegation replacement – Under EIP‑7702 a compromised EOA can re‑delegate to a fresh contract, stripping the original policy before it can act.

If the limit lives where the balance changes—the ERC‑20 token contract or a system contract consulted for ETH withdrawals—every outbound flow (direct transfers, `transferFrom`, burns) is subject to the same check, regardless of the caller.

---

Sketch of an Asset‑Level Design

Core primitives

  • Per‑account spending limit – A rolling‑window cap (e.g., 1 ETH per 24 h). Changes that weaken security (raising or removing a limit, changing a recovery address) are delayed (≈14 days); strengthening changes apply instantly.
  • Irreversible `declareCompromised()` – Once called, normal outflows are blocked.
  • Savior address – Pre‑registered, becomes active only after compromise and can sweep remaining funds. It can be a contract that validates post‑quantum signatures, keeping heavy crypto off the hot path.

ERC‑20 token implementation (no protocol change)

```solidity interface IERC20SecurityExtended is IERC20 { function setSpendingLimit(uint256 amount, uint64 period) external; function saviorTransfer(address account, address to, uint256 amount) external returns (bool); } ``` The token checks the limit on every balance‑decreasing call. A shared registry contract could store the compromised flag and savior address, allowing a user to register once per chain instead of per token.

Native ETH (requires a system contract)

A fixed‑address system contract, following the EIP‑7002 pattern, would be consulted on every ETH debit: ```solidity interface IAccountSecurity { function setSpendingLimit(uint256 amountPerPeriod, uint64 period) external; function declareCompromised() external; function setCompromisedSavior(address savior) external; function saviorSweep(address account, address destination, uint256 amount) external; } ``` Configuration is a normal signed transaction; the protocol invokes the contract on each balance decrease.

---

Benefits and Trade‑offs

| Aspect | Asset‑Level | Account‑Level | |--------|-------------|--------------| | Allowance bypass | Closed – every `transferFrom` hits the limit check. | Open – allowances move funds without wallet logic. | | Delegation resilience | Survives re‑delegation; the token still enforces limits. | Vulnerable – a new delegate can drop the policy. | | Complexity | Requires token‑level state or a system contract; per‑token UX fragmentation. | Simpler – single wallet UI, no token changes. | | Scalability | Additional storage reads on each transfer; may affect mempool validation cost. | No extra per‑transfer overhead. | | Recovery flexibility | Savior can be a contract with advanced verification; irreversible compromise flag prevents key‑reset abuse. | Recovery usually tied to the same wallet logic; may be easier to misuse. | | Native ETH feasibility | Needs consensus‑layer hook or a system contract—significant engineering effort. | Already covered by account‑abstraction modules. |

---

Open Questions Worth Exploring

  1. Prior art – Are there existing asset‑side rate‑limit proposals (e.g., guardian modules) that could be leveraged?
  2. Allowance mitigation – Can wallet‑side designs (e.g., revoking allowances on limit breach) make asset‑level checks redundant?
  3. Griefing risk – An attacker could freeze an account by calling `declareCompromised()`. Is the trade‑off acceptable when a savior is available?
  4. ETH enforcement point – Without a consensus change, can a system contract reliably intercept all ETH outflows, or will it remain an application‑layer feature?
  5. Mempool impact – Does the extra storage touch for each token transfer fit within current mempool validation budgets?
  6. User experience – How to present per‑token limits without overwhelming users? Might a unified registry UI mitigate fragmentation?

---

Practical Takeaway

Asset‑level spending limits address genuine attack vectors that account‑level policies miss, especially allowance bypass and delegation attacks. However, they bring added on‑chain state, potential performance penalties, and a more fragmented user experience. For ERC‑20 tokens, a modest registry contract can implement the idea without protocol changes, making it a viable experimental upgrade. For native ETH, the cost and complexity of a system‑contract hook may outweigh the benefits unless a broader account‑abstraction upgrade is already planned.

Recommendation – Start with a token‑level prototype using a shared registry to gauge security gains versus UX and gas overhead. If the results are compelling, consider lobbying for a native‑ETH system contract in a future EIP, while keeping an eye on mempool scalability and griefing mitigation strategies.

Source

Read original source

Why we picked this

Technical discussion of asset-level rate limiting and EIPs is core protocol governance and security research.

← Back to all articles