Revert Finance V3Utils Dynamic swapRouter Arbitrary-Call Approval Drain (2023-02-18)
- Name: Revert Finance V3Utils dynamic
swapRouterarbitrary-call approval drain - Date: 2023-02-18
- Protocol: Revert Finance
- Chain: Ethereum + Optimism + Polygon + Arbitrum
- Category: arbitrary external call / token-approval drain / router-validation failure / aggregator integration trust-boundary failure
- Loss: just under $30,000 total, later fully refunded by the team
- Summary: Revert’s newly released
Atomic swaps into V3 positionsflow used a V3Utils helper that trusted dynamic swap execution parameters returned by an external aggregator API. The API response carried two especially sensitive values — the swap router contract and the contract to approve for the swap — and Revert preserved them as mutable integration data rather than as a narrowed onchain authorization object. In the exploited path, attacker-shapedswapRouterdata turned V3Utils into an arbitrary caller against token contracts. Because some users had granted V3Utils token allowances larger than the exact amounts needed for their deposits, the attacker could use V3Utils as the approved spender and drain the leftover approved balances directly from user wallets. - Trigger / entry point:
- The affected surface was Revert’s recently released
Atomic swaps into V3 positions/single token add liquidityflow, implemented through V3Utils deployments on Ethereum, Optimism, Polygon, and Arbitrum. - Revert’s postmortem says the frontend called an external swap aggregator API and forwarded two dynamic values into V3Utils: the contract to use as router for the swap and the contract to approve for the swap.
- Revert explicitly identified the
swapRouterparameter as the attack vector: the swap function was invoked with a token contract such as USDC supplied as router, which let the attacker execute arbitrary code against that token contract with V3Utils as caller. - Smart Contract Hacking’s incident catalog preserves one representative Ethereum attacker EOA
0x38F887a0FE01b9e4960D5C727519408fA7f32F70, a malicious helper contract0xd346f652a56d149d585b5447851928f42f61fb27, and example exploit transaction0xdaccbc437cb07427394704fbcc8366589ffccf974ec6524f3483844b043f31d5.
- The affected surface was Revert’s recently released
- Root cause:
- Revert treated offchain aggregator output as trusted execution authority inside an onchain helper that already held user token allowances.
- The postmortem preserves the exact design bet: because the upstream aggregator could change its router and approval-target contracts over time, Revert chose not to hard-code those addresses into an ownerless, immutable V3Utils deployment.
- That flexibility collapsed the real authorization boundary. The critical invariant should have been
only a narrow, authenticated swap route may choose which counterparty V3Utils talks to, which approval target receives temporary spend rights, and which selector family that route may exercise; instead, attacker-shaped API-derived values could redirect execution into arbitrary token calls. - Put differently, the aggregator response was acting as a live route-policy oracle for a contract that already had wallet-spender authority. Once that response could redefine router identity without a stricter onchain commitment, V3Utils stopped being a bounded swap helper and became a generic approval-funded execution surface.
- This was not a treasury-custody failure. The value sat in user wallets, and the failed security boundary was V3Utils’ spender authority plus the missing route-local object that should have kept payer scope, approved router target, approval target, selector family, and swap meaning fused above raw allowance.
- Exploit path:
- Users interacted with Revert’s atomic-swap liquidity flow and approved V3Utils to pull deposit tokens.
- In some cases those approvals exceeded the exact amount consumed for the intended deposit, leaving nonzero residual allowances on the V3Utils contracts.
- The attacker crafted malicious swap parameters so the vulnerable V3Utils path treated a token contract as the
swapRoutertarget. - V3Utils then executed the attacker-shaped call against that token contract while acting as the caller / approved spender.
- The token contract honored
transferFrom()against victim wallets because V3Utils already held allowance. - The attacker repeated the same pattern across Revert’s four chain deployments until the UI flow was disabled and users started revoking approvals.
- What the attacker needed to be true:
- Victims needed lingering token allowances to one of the vulnerable V3Utils deployments.
- The vulnerable atomic-swap path had to remain reachable with dynamic router / approval information derived from the external aggregator response.
- There could not be a strict onchain allowlist proving the router was a legitimate swap integration or that the calldata matched the intended swap semantics.
- The attacker only needed enough chain-specific funding to submit the draining transactions; the value source was pre-existing user approvals.
- Technical anatomy worth remembering:
- This was a clean example of a protocol that did not permanently custody user funds still becoming a wallet-drain primitive because it combined allowance authority with externally shaped execution.
- The dangerous design choice was not merely
dynamic aggregator support; it was letting mutable offchain router metadata and mutable approval-target metadata cross the trust boundary into a contract that could already spend user assets. - Revert’s own postmortem is especially useful because it states the exact mistaken assumption: the team avoided hard-coding router and approval targets because the upstream aggregator could change them over time.
- That flexibility turned into an authorization bug. Once token contracts themselves could be substituted into the router slot, V3Utils stopped being
an immutable swap helper honoring one route familyand becamean already-approved contract willing to speak in attacker-chosen calldata. - In the exploit-pattern taxonomy, Revert is the crisp
offchain route metadata became live policysubvariant: the dangerous execution surface was not a raw spender-local multicall primitive, but the protocol’s choice to import mutable aggregator fields as sovereign route authority. - The sharper healthy-side lesson is not just
validate the router address; it istreat router identity, approval target, allowed selector family, payer scope, and route meaning as one route-local approval object before any allowance-backed execution happens. - The positive-design handoff is therefore approval-funded route authority as a single security object: V3Utils had raw allowance, but no narrower route-local object kept payer scope, approved router target, approval target, selector family, and swap meaning fused once the aggregator response crossed onchain.
- Representative transactions and addresses:
- Vulnerable V3Utils deployments called out by Revert and Revoke.cash:
- Ethereum:
0x531110418d8591C92e9cBBFC722Db8FFb604FAFD - Polygon:
0x8C925768C793e00C095135b8656d6014eE2d07bB - Optimism:
0x2A017f2Fb369F4CA061B8D8A922Bb05100e8f8C3 - Arbitrum:
0x95a8Cc9ab71b26BDaCbe6a7cCf519456eDc2a164
- Ethereum:
- Representative Ethereum attacker EOA:
0x38F887a0FE01b9e4960D5C727519408fA7f32F70 - Representative malicious helper contract:
0xd346f652a56d149d585b5447851928f42f61fb27 - Representative Ethereum exploit transaction:
0xdaccbc437cb07427394704fbcc8366589ffccf974ec6524f3483844b043f31d5 - Revert’s postmortem also names additional chain-specific attacker EOAs: Polygon
0x31487bBD8c2E79465940CC07DEc049dC0845331B, Optimism/Arbitrum0xF93CEE8fdbC59BcA3A74A2F08d60a3e366E4768c, Optimism0x9b6Bb041B79B83b2dF1839d51Cd654cc6444e852, and Optimism0x5504790a2426A312194270C7a7443a54AF9F75AE.
- Vulnerable V3Utils deployments called out by Revert and Revoke.cash:
Three approval-route promotions this incident collapsed
-
V3Utils already held real spender reach into user wallets
- Users had granted the V3Utils deployments live ERC-20 allowances so the helper could pull swap input tokens for the atomic deposit flow.
- In a nontrivial number of cases those approvals exceeded the exact amount needed for the intended liquidity action, which meant raw wallet-spend reach still existed even after honest use.
-
Mutable aggregator metadata was still promoted into canonical route authority
- Revert’s frontend imported external aggregator output for the
swapRouterand approval-target contracts, then forwarded those values into V3Utils as if they still described one legitimate swap route. - That collapsed
an API returned some integration metadataintothis exact metadata object may define who V3Utils is allowed to approve and call while spending against user allowances, even though no stricter onchain route commitment had authenticated router identity, approval target, selector family, or settlement meaning together.
- Revert’s frontend imported external aggregator output for the
-
That accepted route object still inherited ordinary downstream approval-and-call power
- Once the metadata survived ingress, V3Utils continued to exercise its normal allowance-backed execution authority against the attacker-chosen target.
- That final promotion is what turned leftover approvals into theft: a route object that should only have described one swap step still retained enough downstream authority to make a token contract honor
transferFrom(victim, attacker, amount)from V3Utils’ approved spender context.
- Response / mitigation:
- Revert disabled the atomic-swap feature as soon as the issue was identified and began broadcasting the vulnerable contract addresses so users could revoke approvals.
- The team added site warnings, coordinated revoke messaging across social channels, and tracked remaining exposed allowances in real time.
- On the following day Revert deployed its own countermeasure contract and ran a whitehat bot to preemptively sweep some still-exposed balances for later refunding.
- Revert later refunded affected users via chain-specific airdrops and subsequently says V3Utils underwent a PeckShield audit and redeployment with enhanced security measures.
Pattern tags
- arbitrary-call-token-approval-drain
- Offchain route metadata became live approval policy: Revert’s external aggregator response supplied the
swapRouterand approval-target contracts, so mutable integration metadata crossed the trust boundary and directly decided who V3Utils would approve and which contract it would call - Dynamic router identity outran route authentication: once a token contract could occupy the
swapRouterslot, V3Utils stopped beingan immutable swap helper for one quoted route familyand becamean already-approved contract willing to speak attacker-shaped calldata - Existing wallet approvals funded the exploit path: victims had already approved V3Utils, so the attacker only needed Revert to truthfully exercise that spender identity against attacker-chosen targets rather than compromise treasury custody
- Integration flexibility displaced one route-local authority object: router identity, approval target, selector family, payer scope, and settlement meaning were preserved as mutable API output instead of one narrower onchain authorization object above raw allowance
Why the exploit worked
- Revert’s own postmortem preserves the key failed design bet: the team did not hard-code the router and approval-target contracts because the external aggregator could change them over time. That meant mutable upstream integration data quietly became sovereign route authority inside a contract that already held user token approvals.
- The exploit therefore was not just
arbitrary router validation missing; it wasapproval-funded execution imported its own policy from outside the contract. Once attacker-shaped metadata could replace the router with a token contract, V3Utils became an arbitrary caller from the perspective of wallets that had already approved it. - The attacker did not need to counterfeit market prices, compromise admins, or find funds inside protocol custody. They only needed V3Utils to use its legitimate spender identity while speaking attacker-shaped calldata to a target that should never have been considered a swap router in the first place.
- Revert is especially clean as a family exemplar because the dangerous object was neither a naked multicall helper nor a hidden upgrade regression. It was the external aggregator response itself acting as a live route-policy oracle for who gets approval, who gets called, and what that call is allowed to mean.
- That makes the preserved healthy-side lesson sharper than
validate router address: once a wallet-approved helper can import route metadata, that metadata must be narrowed into one authenticated route-local object before any allowance-backed execution begins.
Detection and audit pivots
- Trace every path where offchain quote or aggregator responses can choose a router address, approval target, or calldata that survives into onchain execution. The high-signal red flag is
API-derived address becomes approve target or call targetinside a contract that commonly sits in users’ allowance tables. - Add a negative test for the exact Revert failure shape: can a token contract occupy the
swapRouterslot, and can attacker-crafted calldata make the wallet-approved helper executetransferFrom(victim, attacker, amount)from its own context? If yes, the contract is already a wallet drainer. - Review dynamic integration support as an authorization problem, not a compatibility feature. If the reason a field stays mutable is
the upstream aggregator may change it later, that field is already part of the spender authority object and must be allowlisted, committed, or otherwise narrowed before use. - Monitor wallet-approved helper transactions where downstream call targets are ERC-20 token contracts or other noncanonical router addresses instead of the expected small set of DEX integrations. In Revert-style systems, token-contract targets are the anomaly that matters.
- Inventory which deployed contracts can currently exercise each public spender address users approve. Incident response needs to answer
which integration metadata paths can spend with this approval right now?before attackers do.
Failure-prevention checklist
- Do not let offchain quote or aggregator responses directly choose the final onchain router address, approval target, or selector family for an already-approved helper. Those fields need to be hard-pinned, tightly allowlisted, or committed into one route-local authorization object before execution.
- Keep
who receives approvalandwho gets calledfused to one reviewed route family. If a swap path needs dynamic price or path data, that does not imply it also needs dynamic authority over router identity. - Fail closed on non-router targets. Token contracts, EOAs, fresh deployments, and contracts outside the small canonical integration set should be rejected before any allowance-backed call path begins.
- Scope approvals to exact amounts, single actions, and short lifetimes wherever possible so leftover wallet authority does not remain available after the intended deposit is complete.
- Treat helper contracts that only hold temporary user approvals as custody-relevant anyway. Revert’s own postmortem is a good reminder that
we do not custody user fundsis not a valid reason to skip the same review and audit discipline applied to vault-like contracts. - Add route-shape invariant tests that simulate malicious aggregator metadata: token contract in the router slot, approval target mismatch, calldata selector drift, and attacker-controlled recipient substitution.
- Monitor chain-by-chain allowance exposure for every public helper contract and prepare spender-specific revoke guidance in advance, because the first containment question is often
which already-approved contract can still be abused right now? - If dynamic integrations must exist, split observation from authority: offchain systems may suggest routes, but an onchain contract with wallet spend rights should only execute against a precommitted set of routers, approval targets, and call semantics.
Audit prompts
- Which exact offchain fields can currently influence
approve()targets, downstream call targets, selector choice, recipient, or token pull scope for each wallet-approved helper? - If an aggregator or quote server changed router metadata right now, what onchain check would stop that new address from inheriting wallet spend authority immediately?
- Can any ERC-20 token contract, EOA, proxy with mutable implementation, or freshly deployed contract occupy a router slot today? If not, where is that prevented and how is the allowlist governed?
- Are router identity, approval target, payer scope, token pair, selector family, and settlement meaning authenticated as one route-local object, or are they still assembled piecemeal from mutable API output?
- After the intended deposit executes, what residual wallet authority remains to the helper contract, and how quickly can operators measure, publicize, and help users revoke it across all deployments?
- Do tests cover the exact negative case
wallet-approved helper + attacker-shaped route metadata = token.transferFrom(victim, attacker, amount)on every supported chain and integration family? - If a helper is described internally as
non-custodial, what review step still proves it cannot redirect already-approved wallet authority into arbitrary downstream calls? - Which contracts, APIs, config blobs, or frontend paths can silently broaden the effective call surface of a spender users already trust?
Lessons that should already be standard
- Dynamic quote metadata is not harmless convenience once it chooses who an already-approved contract may speak to. In Revert, mutable router identity was already wallet authority.
We only touch user funds temporarilyis still a custody statement. A helper that can pull tokens from wallets is inside the money boundary even if balances do not linger in protocol storage.- Approval safety depends on route authentication, not only spender identity. A user approving V3Utils did not mean V3Utils was free to call any contract that an API response later named
router. - The dangerous object is often larger than one address. Router identity, approval target, selector family, payer scope, and settlement meaning have to stay fused, or raw allowance turns flexible integrations into wallet-drain surfaces.
- Exact-amount approvals and fast revoke messaging materially contained this incident. Narrow authority and prepared incident tooling are part of protocol design, not only comms hygiene.
- Offchain integration flexibility should default to less onchain authority, not more. If upstream services need to evolve quickly, the spender-bearing contract should become stricter about what kinds of downstream execution it will honor.
- Small incidents are still worth preserving when they isolate the mechanics cleanly. Revert shows the approval-drain family in a form where the policy failure is unusually legible: imported route metadata quietly outranked the user’s intended swap meaning.
Why this incident matters
- It is an early, crisp example of the approval-drain family later seen in Dexible, Socket, LI.FI, Dough, and related router / aggregator incidents, but with the extra clarity of a team postmortem that names the exact flexibility tradeoff that failed.
- The loss stayed relatively small only because the feature was new, many approvals were exact-amount rather than infinite, and the team moved quickly to force revocations before the low-hundreds-of-thousands of still-open allowances could be harvested more fully.
- The preserved healthy-side lesson is sharper than
hard-code routers: once a contract already has wallet-spender authority, every dynamic route field that can alter counterparty, approval target, selector family, or settlement meaning is itself part of the authorization object and must be narrowed before onchain execution. - Revert is especially worth preserving because the exploit path is mechanically clean: offchain route metadata became live onchain policy,
swapRouterbecame an arbitrary call surface, and standing wallet approval supplied the balance sheet. - It is also a useful contrast case to Seneca-style spender-local generic execution: Revert’s spender was made dangerous by imported route metadata, not by exposing a naked low-level call language as a first-class local primitive.
Internal linkages
- Core recurring pattern: arbitrary-call-token-approval-drain.
- Positive-design companion: approval-funded-route-authority-truth-security-object — the clean follow-up when the sharper question is what one route-local object should have kept spender identity, payer scope, approved router target, approval target, selector family, and settlement meaning fused above the user’s standing approval.
- Broader spender-boundary companion: spender-authority-truth-security-object — useful when the sharper question is why any already-approved helper should be allowed to exercise wallet allowance from attacker-shaped route metadata at all.
- Adjacent pattern: approval-by-address-mutable-code-execution — useful contrast because Revert’s problem was not a mutable approved proxy address, but a contract that let offchain routing metadata choose execution targets too freely.
Control surface
-
The decisive leverage sat in V3Utils’ existing spender approvals, the external aggregator response acting as a de facto policy oracle, the dynamic router / approval-target values imported from that response, and the absence of strict onchain validation binding those fields to one legitimate swap path.
-
The deeper missed control was the lack of a narrower route-local authority object proving
this exact wallet-funded swap may approve this exact target and call this exact router / selector family for this exact settlement meaningbefore any allowance-backed execution happened. -
Once the token contract itself could occupy the router slot, V3Utils stopped being a swap helper and became an attacker-directed wallet drainer.
-
Sources:
-
Last reviewed: 2026-08-05 UTC