Convergence Finance CvxRewardDistributor claimMultipleStaking() Fake-Staking-Contract Reward Mint (2024-08-01)
- Name: Convergence Finance CvxRewardDistributor
claimMultipleStaking()fake-staking-contract reward mint - Date: 2024-08-01
- Protocol: Convergence Finance
- Chain: Ethereum
- Category: account confusion / fake integration endpoint / reward-source substitution / unbounded reward mint
- Loss: about
$210k-$212kof realized dump value plus roughly$2kof stolen unclaimed Convex rewards; roughly58M CVGminted and dumped, plus a much larger collapse in CVG market value - Summary: Convergence’s reward distributor proxy (
0x2b083beaaC310CC5E190B1d2507038CcB03E7606) let anyone callclaimMultipleStaking()with an arbitraryclaimContractsarray. The protocol meant those addresses to be canonical Convergence staking-position contracts implementingclaimCvgCvxMultiple(address), but the function never proved that assumption onchain. The attacker deployed malicious contract0xee45384d4861b6fb422dfa03fbdcc6e29d7beb69, inserted it as the only claim source, and made it return an absurd CVG claim amount. The deeper failure was not justfake staking contract in, big number out; it was that the whole certifier-to-mint chain stayed caller-authored: the attacker chose the reward-certifying contract, that contract authored the claimed amount, and the distributor then treated that return value as canonical reward entitlement worthy of minting about58M CVG. The attacker dumped the fresh supply through Curve pools for roughly60 WETHand15,900 FRAX, collapsing CVG by about99%. - Trigger / entry point:
- The vulnerable surface was
CvxRewardDistributor.claimMultipleStaking(ICvxStakingPositionService[] claimContracts, address _account, uint256 _minCvgCvxAmountOut, bool _isConvert, uint256 cvxRewardCount). - That path was meant to aggregate claims across multiple legitimate Convergence staking contracts.
- Instead of deriving or checking a canonical allowlist / registry of staking-position services, it accepted a caller-supplied
claimContractsarray and called each contract through the expected interface. - Multiple incident reconstructions state that a post-audit modification removed the line that had checked the supplied input, turning the surface from a convenience helper into a mint primitive.
- The vulnerable surface was
- Root cause:
- Convergence authenticated the wrong object. It treated an arbitrary caller-supplied contract as if it were a canonical staking-reward source.
- The validation failure was not merely
bad math; it was a provenance failure. The distributor never proved that the contract reporting claimable rewards belonged to Convergence’s real staking graph. - The broken security object was the whole certifier-to-mint path:
claimContracts[]selected the certifier,claimCvgCvxMultiple()authored the claimed amount, and downstream mint logic treated that return value as if it already described a real Convergence reward entitlement. - Once that fake root object was accepted, the attacker did not need a second bug. The counterfeit certifier could directly manufacture mint-worthy reward truth.
- The incident is therefore closer to fake integration endpoint / root-of-trust substitution than to a generic accounting typo: the protocol let the attacker choose who was allowed to define reward reality.
- Exploit path:
- The attacker deployed malicious helper contract
0xee45384d4861b6fb422dfa03fbdcc6e29d7beb69implementing the expectedclaimCvgCvxMultiple(address)signature. - They called
CvxRewardDistributor.claimMultipleStaking()in transaction0x636be30e58acce0629b2bf975b5c3133840cd7d41ffc3b903720c528f01c65d9and supplied that malicious contract as the sole entry inclaimContracts. - When the distributor called the fake staking contract, the malicious implementation returned a gigantic synthetic CVG claim amount. Public PoCs reproduce this as
type(uint256).max - CVG.totalSupply(). - Because the distributor never verified that the contract was one of Convergence’s real staking modules, it accepted the return value as authentic earned rewards.
- The contract then minted roughly
58M CVGto the attacker-controlled account. - The attacker immediately dumped the minted tokens through Curve CVG/ETH pool
0x004C167d27ADa24305b76D80762997Fa6EB8d9B2and Curve CVG/FRAX pool0xa7B0E924c2dBB9B4F576CCE96ac80657E42c3e42. - The cash-out realized about
60 WETHand15,900 FRAX, while the much larger damage landed on CVG holders through near-total price collapse.
- The attacker deployed malicious helper contract
- What the attacker needed to be true:
claimMultipleStaking()had to accept arbitrary staking-contract addresses from the caller.- The reward distributor had to trust the return value of
claimCvgCvxMultiple()without proving the caller-supplied contract was canonical. - The mint path had to convert returned claimable amount directly into new CVG supply.
- Enough secondary-market liquidity had to exist in CVG/ETH and CVG/FRAX pools for the attacker to crystallize value before the token repriced to near zero.
- Technical anatomy worth remembering:
- This was a reward-certifier authenticity failure. The decisive bug was not just
missing input validation; it was that Convergence let a user choose the contract that would certify how much reward had been earned. - The malicious contract only needed interface compatibility, not protocol legitimacy. That makes this a clean fake-integration-endpoint case.
- The full trust chain stayed attacker-authored: choose fake staking contract → satisfy the expected interface → return a fabricated
(cvgClaimable, rewards[])tuple → let the distributor convert that tuple into real minted CVG. That is the sharper preserved mechanism thanunvalidated array parameteralone. - The fake contract did not need reentrancy, governance capture, or oracle games. It simply returned a fabricated claim amount and relied on Convergence to treat that fabricated amount as protocol truth.
- The preserved invariant is:
reward source,reward entitlement, andmint amountmust come from canonical protocol state together. If the user can choose the reward-certifying contract, the mint path is already compromised. - The post-audit-change detail matters because it shows how easily a protocol can fall out of audit scope: a single removed validation line converted a reward helper into an unbounded emission printer.
- This was a reward-certifier authenticity failure. The decisive bug was not just
- Representative transactions and contracts:
- Main exploit transaction:
0x636be30e58acce0629b2bf975b5c3133840cd7d41ffc3b903720c528f01c65d9 - Exploiter EOA:
0x03560a9d7a2c391fb1a087c33650037ae30de3aa - Malicious fake staking contract:
0xee45384d4861b6fb422dfa03fbdcc6e29d7beb69 - Vulnerable reward distributor proxy:
0x2b083beaaC310CC5E190B1d2507038CcB03E7606 - Reported implementation contract:
0x47c69e8c909ce626Af73c955A5e34A20B7c71f19 - CVG token:
0x97efFB790f2fbB701D88f89DB4521348A2B77be8 - Cash-out pools used in public PoCs: Curve CVG/ETH
0x004C167d27ADa24305b76D80762997Fa6EB8d9B2and Curve CVG/FRAX0xa7B0E924c2dBB9B4F576CCE96ac80657E42c3e42
- Main exploit transaction:
- Response / mitigation:
- Convergence publicly acknowledged the incident and warned users to withdraw staked assets as a precaution while the team investigated.
- Public incident analyses report — and later postmortem quotations preserve — that the team attributed the exploit to a post-audit gas-optimization change that removed the line validating the input given to the function.
- The practical lesson was not only
audit more; it wastreat post-audit trust-boundary changes as new unaudited code, especially when they decide which external contracts may certify rewards or balances.
Pattern tags
- account-confusion-root-of-trust-substitution
- issuance-redemption-solvency-asymmetry
- The exploit only needed one counterfeit trust anchor: once
claimMultipleStaking()let the attacker choose the contract that defined reward truth, the honest downstreammint transferable CVG -> dump into live poolsleg converted that local authentication error into real value extraction. - This is a clean fake-integration-endpoint incident rather than a generic
reward math bug. The decisive question was not how CVG emissions were computed after a canonical source spoke, but who got to speak as the canonical source in the first place.
Why the exploit worked
- Convergence collapsed three different questions into one helper-shaped assumption:
what exact contract is allowed to certify staking rewards?,what exact claim does that contract certify for this account?, anddoes the certified amount deserve immediate mint authority against CVG supply?The attacker only had to win the first question with their own contract for the other two to be granted implicitly. Implements claimCvgCvxMultiple(address)was treated as if it were a provenance proof. That authenticated an attacker-deployed endpoint as a legitimate reward source even though the protocol had not pinned the address to a registry, immutable config slot, or other protocol-owned staking graph.- Once the counterfeit certifier was accepted, the rest of the path stayed locally honest. The fake contract returned a giant claim amount, and the distributor faithfully converted that returned value into fresh CVG plus associated reward transfers. No oracle manipulation, reentrancy, or secondary authorization bug was needed after the root-authenticity failure.
- The exit side stayed economically real. Freshly minted CVG still had immediate swap access against live Curve liquidity, so one counterfeit reward-certifier call became both an emission blowout and an instantly realizable treasury / market-structure loss.
- This makes Convergence a strong corpus exemplar: the protocol did not merely forget an input check. It let a caller-authored external contract graduate from
helper-shaped address in calldatatocanonical reward truthand then tomint-worthy supply authority.
Three reward-certifier promotions this incident collapsed
Some interface-compatible staking / claim contract existsis not the same thing asthis exact contract is the canonical Convergence reward-certifier for this user's staking position.This contract returned one claim tupleis not the same thing asthat tuple is the canonical earned-reward entitlement the protocol should publish as CVG truth.That accepted entitlement existsis not the same thing asit still deserves immediate mint authority plus live secondary-market cash-out against CVG liquidity right now.- Convergence collapsed those promotions into one helper-shaped assumption: if the caller supplied a contract with the right signature, the protocol let that contract define entitlement reality and then immediately monetized that reality through fresh supply creation.
Detection and audit pivots
- Enumerate every reward, staking, or emission helper that accepts caller-supplied contracts, adapters, or position services. Ask which of those surfaces are protocol-pinned and which are only interface-compatible.
- Diff single-claim and batch-claim code paths for registry or allowlist checks, especially after gas-optimization refactors. Convergence is a reminder that deleting one validation line can silently move a path from
aggregatortomint printer. - Fuzz reward helpers with hostile interface-compatible contracts that return
type(uint256).max, absurd but in-range emission values, duplicated reward arrays, or empty reward arrays plus giant mint claims. The question is not just whether the call reverts, but whether any returned tuple can become live emission truth without provenance proof. - Trace where reward entitlement is actually bound to canonical protocol state. If the system can reach
mint(amount)before provingthis exact contract was allowed to certify this exact user's claim, the entitlement path is still caller-authored. - Monitor for
unexpected certifier address,impossible one-call emissions,single-call total-supply jumps, andfresh mint immediately swapped into secondary-market pools. Convergence-style incidents often advertise themselves in those control-plane signals before governance or users can reason through the full exploit.
Failure-prevention checklist
- Derive eligible staking / reward-source contracts from canonical protocol state inside
claimMultipleStaking()rather than letting the caller supply the contracts that define reward truth. - If the protocol truly needs caller-supplied selectors, validate each one against a registry or immutable allowlist before any external call or reward accumulation begins.
- Keep
reward source identity,reward entitlement identity, andmint amountinside one fused security object. The same path that decides which contract may certify rewards should also constrain what account, tranche, and amount that contract is allowed to certify. - Make the batch helper resolve reward rights through one shared internal consume primitive instead of treating each external contract return value as self-authenticating payout truth.
- Treat interface compatibility as a necessary but weak property.
implements claimCvgCvxMultiple(address)should never be enough to earn mint authority. - Gate post-audit changes that touch trust boundaries, registries, helper inputs, or downstream mint logic behind fresh review, explicit diff signoff, and regression tests.
- Add regression tests for counterfeit certifier contracts, duplicate batch members, absurd return amounts, and
new contract returns max uintstyle payloads. - Bound loss even if one fake certifier slips through once: per-call mint caps, emission-rate limits, route-local pauses, delayed conversion, or staged settlement should stop one counterfeit claim source from printing protocol-scale supply instantly.
- Monitor reward minting against expected emission envelopes and page operators on impossible jumps in one call, one account, or one helper route.
Audit prompts
- What exact canonical object proves that one contract is allowed to certify CVG rewards for one user, and where is that identity loaded or recomputed before
claimMultipleStaking()trusts it? - Can a caller supply a contract that is interface-compatible but not protocol-canonical, and if not, which concrete registry or immutable config blocks it?
- Where does the code bind
reward-certifying contract,claimable amount, andmint recipientinto one machine-checkable entitlement instead of trusting a raw external return tuple? - If a malicious contract returned
type(uint256).max, what invariant would stop that value from becoming live minted supply? - Do single-claim and batch-claim cousins share one internal entitlement-consumption primitive, or are reviewers relying on several manually synchronized code paths?
- Which post-audit edits changed who or what is allowed to define reward reality, and were those edits reviewed as security-boundary changes rather than convenience refactors?
- What onchain control caps or delays issuance if one reward-certifier path lies once?
- Which production monitor would have fired first: unexpected helper address, impossible per-call emission, anomalous total-supply jump, or oversized conversion into secondary-market pools?
Lessons that should already be standard
- Reward distributors are often mint authorities in disguise. If they trust external contracts to report entitlement, those contracts belong inside the mint threat model.
- The protocol must choose the reward certifier, not the user. Once the caller can nominate the contract that defines earned rewards, every later check can be locally correct and still be attacker-authored fiction.
Implements the right interfaceis not a provenance proof. Counterfeit certifiers monetize exactly because interface shape is cheaper to fake than protocol legitimacy.- Post-audit trust-boundary edits are new unaudited attack surface. A single removed validation line can convert a helper into an emission printer.
- Emission systems need bounded-loss design too. Even when the printed asset is the protocol’s own token, the balance-sheet damage can spread through market structure, governance weight, and user solvency far beyond the attacker’s immediate cash-out.
- Impossible reward amounts should fail structurally, not only socially. If a single helper call can ask the protocol to mint essentially the remaining uint256 supply space, the entitlement boundary is already underdesigned.
Why this incident matters
- It is a concise EVM example of account confusion outside the more familiar fake-collateral or fake-market cases. Here, the attacker supplied a counterfeit reward source rather than counterfeit collateral.
- It shows that yield and staking distributors can carry the same root-of-trust problem as bridges, vault migrators, and lending helpers: once a caller chooses the object that defines reality, every later check can be perfectly consistent and still be attacker-authored fiction.
- It also preserves an important market-structure lesson: even when realized cash-out is only a few hundred thousand dollars, the protocol can still suffer a much larger governance-token integrity failure once unbacked emissions hit live secondary-market liquidity.
Internal linkages
- Core recurring pattern: account-confusion-root-of-trust-substitution.
- Positive-design companion: root-object-authenticity-and-trust-anchor-recomputation-discipline — the healthier boundary starts even earlier than reward math: the protocol should have recomputed or loaded the one canonical staking / reward-source root object before any caller-supplied contract got to define entitlement reality.
- Positive-design companion: reward-entitlement-identity-and-batch-canonicalization — the healthier boundary is one canonical reward-entitlement object where certifier identity, distinct claim right, and mintable amount stay fused before any batch helper pays or mints.
- Adjacent pattern: balance-delta-provenance-failure — both families confuse a local signal with authentic reward entitlement, but Convergence failed earlier by trusting the wrong contract to define reward truth in the first place.
- Adjacent pattern: issuance-redemption-solvency-asymmetry — once fake reward accounting can mint real transferable supply, secondary-market cash-out becomes the reserve side that still honors the printed claim.
Control surface
-
The leverage sat in the full certifier-to-mint chain inside
claimMultipleStaking(): the caller-suppliedclaimContractsarray, the fakeclaimCvgCvxMultiple()endpoint, the attacker-authored(cvgClaimable, rewards[])tuple, and the downstream CVG mint path that treated those values as authentic earned rewards. -
Convergence did not mainly fail because Curve liquidity was thin or because the attacker found a complex arithmetic edge. It failed because the protocol let the attacker choose the contract that defined how much CVG had been earned and then let that same counterfeit certifier feed mint-worthy reward truth straight into supply creation.
-
Sources:
- https://etherscan.io/tx/0x636be30e58acce0629b2bf975b5c3133840cd7d41ffc3b903720c528f01c65d9
- https://etherscan.io/address/0x03560a9d7a2c391fb1a087c33650037ae30de3aa
- https://etherscan.io/address/0x2b083beaaC310CC5E190B1d2507038CcB03E7606
- https://etherscan.io/address/0x47c69e8c909ce626Af73c955A5e34A20B7c71f19#code
- https://raw.githubusercontent.com/SunWeb3Sec/DeFiHackLabs/main/src/test/2024-08/Convergence_exp.sol
- https://blog.verichains.io/p/convergence-incident-how-a-simple
- https://blog.solidityscan.com/convergence-finance-hack-analysis-12e6acd9ea08/
- https://blockapex.io/convergence-finance-hack-analysis/
- https://smartcontractshacking.com/hacks/convergence-hack-2024
- https://quadrigainitiative.com/casestudy/convergencefinancerewarddistributormintingexploit.php
-
Last reviewed: 2026-07-31 UTC