EGD Finance Reward Oracle Manipulation
Exploit Transactions
Victim Addresses
0x34bd6dba456bc31c2b3393e499fa10bed32a9370BSC0xb0d3586581cbbb41bbc0c74c0d36e607e56251c0BSCLoss Breakdown
Similar Incidents
SellToken Reward Oracle Manipulation
50%BXH Bonus Oracle Manipulation
41%TiFi Oracle Manipulation
41%NovaX TokenStake Oracle Manipulation Exploit
39%Nimbus Spot-Oracle Reward Drain
38%sDAO Reward Inflation Attack
38%Root Cause Analysis
EGD Finance Reward Oracle Manipulation
1. Incident Overview TL;DR
EGD Finance on BSC was exploited through a flash-loan-driven price-oracle manipulation. The attacker first created a small 100 USDT stake through helper contract 0xc30808d9373093fbfcec9e026457c6a9dab706a7, then in transaction 0x50da0b1b6e34bce59769157df769eb45fa11efc7d0e292900d6b0a86ae66a2b3 borrowed USDT from public Pancake pairs, collapsed the EGD/USDT pair's visible USDT balance, claimed vastly inflated EGD rewards from proxy 0x34bd6dba456bc31c2b3393e499fa10bed32a9370, sold the reward tokens back into the market, repaid both flash swaps, and finished with 36044121156865234135946 raw USDT units sent to the exploit caller.
The root cause is that the victim priced EGD rewards from a same-transaction AMM spot ratio. The implementation behind the proxy reads USDT.balanceOf(pair) * 1e18 / EGD.balanceOf(pair) and uses that result inside claimAllReward(). Because those balances are public and flash-loan-manipulable, reward redemption became directly controllable by any unprivileged actor who could temporarily drain the pair's USDT side.
2. Key Background
EGD Finance is fronted by proxy 0x34bd6dba456bc31c2b3393e499fa10bed32a9370 and delegates to implementation 0xb0d3586581cbbb41bbc0c74c0d36e607e56251c0. The implementation is unverified on the explorer, so the collected analysis relies on runtime disassembly and Heimdall decompilation.
Two Pancake pairs matter:
0xa361433e409adac1f87cdf133127585f8a93c67dfor EGD/USDT, which the victim implicitly trusts as its reward-price source.0x16b9a82891338f9ba80e2d6970fdda79d1eb0daefor USDT/WBNB, which provides the outer flash swap and the USDT/WBNB reference used in the portfolio-profit calculation.
The attacker also used a public helper contract whose creation artifact shows hard-coded references to the victim proxy, PancakeRouter, EGD, USDT, and both pairs. The observed lifecycle was: deploy helper, bond and stake 100 USDT, then execute a harvest transaction that performs both flash swaps and the victim reward claim.
3. Vulnerability Analysis & Root Cause Summary
The vulnerability is a manipulable reward valuation path, not a privileged compromise. The decompiled victim implementation exposes getEGDPrice() and computes the reward price from current token balances in the public EGD/USDT Pancake pair. claimAllReward() uses that live price during the same execution path that transfers reward EGD to the caller. That design violates the invariant that reward redemption for a fixed accrued reward value must not depend on transient AMM reserves that an attacker can distort inside one transaction. The concrete breakpoint is the call sequence where the helper first drains almost all USDT from the EGD/USDT pair and then immediately invokes claimAllReward(). Once the denominator in the reward conversion formula collapses, the victim sends millions of EGD instead of a negligible honest-price payout. The exploit is ACT because every step uses public contracts, public liquidity, and ordinary transaction inclusion on BSC.
4. Detailed Root Cause Analysis
The decompiled implementation shows the core pricing logic:
function getEGDPrice() public payable returns (uint256) {
address var_b = address(pair);
...
(bool success, bytes memory ret0) = address(EGD).Unresolved_70a08231(var_b);
...
address var_e = address(pair);
...
(bool success, bytes memory ret0) = address(U).Unresolved_70a08231(var_e);
...
return (0x0de0b6b3a7640000 * var_c.length) / var_c.length;
}
In human terms, the implementation reads the EGD and USDT balances of the Pancake pair and computes USDT.balanceOf(pair) * 1e18 / EGD.balanceOf(pair). The root cause JSON correctly identifies this as the reward oracle used by claimAllReward().
The exploit trace for 0x50da0b1b6e34bce59769157df769eb45fa11efc7d0e292900d6b0a86ae66a2b3 shows the full manipulation path:
TransparentUpgradeableProxy::fallback(0xC30808D9373093fBFCEc9e026457C6a9DaB706a7)
EGD_Finance::calculateAll(...) [delegatecall]
-> return 341874999999972
PancakePair::swap(2000000000000000000000, 0, helper, 0x0000)
helper::pancakeCall(...)
PancakePair::swap(0, 424456221210335857574110, helper, 0x00)
helper::pancakeCall(...)
EGD_Finance::claimAllReward() [delegatecall]
Right before the victim claim, the same trace records the manipulated pair state:
EGD_token::balanceOf(PancakePair: [0xa361433E409Adac1f87CDF133127585F8a93c67d]) -> 52443151506653906536540000
BEP20USDT::balanceOf(PancakePair: [0xa361433E409Adac1f87CDF133127585F8a93c67d]) -> 3193563430091851
EGD_token::transfer(helper, 5614105648378533801366233)
emit Claim(player: helper, amount: 5614105648378533801366233)
Those values match the root-cause calculation. With calculateAll(helper) = 341874999999972 and the manipulated price of roughly 60895719 wei-USDT per EGD, the claim output becomes 5614105648378533801366233 wei-EGD. The same root cause notes that without the flash-loan reserve collapse, the honest-price payout would have been only 42239932861192201 wei-EGD, showing several orders of magnitude of inflation.
The balance-diff artifact independently confirms the economic result:
{
"holder": "0xee0221d76504aec40f63ad7e36855eebf5ea5edd",
"token": "0x55d398326f99059ff775485246999027b3197955",
"delta": "36044121156865234135946"
}
Receipt metadata for the exploit transaction shows gasUsed=650211, effectiveGasPrice=608904494238, and gasCostWei=395916400102984218, which is consistent with the portfolio-profit accounting in root_cause.json.
5. Adversary Flow Analysis
The adversary flow has three stages.
First, EOA 0xbc5e8602c4fba28d0efdbf3c6a52be455d9558f5 deployed helper contract 0xc30808d9373093fbfcec9e026457c6a9dab706a7 in transaction 0xc53e6862a63887d717e0349c5dbd5c5faa333ac6deab13a68bb5b572df879d8c.
Second, in transaction 0x4a66d01a017158ff38d6a88db98ba78435c606be57ca6df36033db4d9514f9f8, the helper bonded an inviter, swapped BNB for exactly 100 USDT, and staked that 100 USDT into the victim to create a position eligible for reward claiming.
Third, in transaction 0x50da0b1b6e34bce59769157df769eb45fa11efc7d0e292900d6b0a86ae66a2b3, EOA 0xee0221d76504aec40f63ad7e36855eebf5ea5edd called the helper's harvest path. The helper borrowed 2000 USDT from the USDT/WBNB pair, then 424456.221210335857574110 USDT from the EGD/USDT pair, leaving only 3193563430091851 raw USDT units in the EGD/USDT pair before the victim claim. It then invoked claimAllReward(), received 5614105648378533801366233 wei-EGD, swapped the reward tokens back for 37327489820496241708668 raw USDT units, repaid the inner and outer flash swaps, and finally transferred 36044121156865234135946 raw USDT units plus leftover BNB dust to the exploit caller.
6. Impact & Losses
The measurable victim-side loss was a drain of reward inventory from the EGD Finance proxy. root_cause.json reports the loss as:
5614105648378533801366233raw EGD units (5,614,105.648378533801366233EGD,decimal=18)36044121156865234135946raw USDT units (36,044.121156865234135946USDT using the incident's accounting basis,decimal=18)
The exploit therefore converted a manipulable reward-claim path into direct economic extraction. The transaction-level balance diff and receipt metadata support both the token drain and the attacker profit figures.
7. References
- Exploit transaction:
0x50da0b1b6e34bce59769157df769eb45fa11efc7d0e292900d6b0a86ae66a2b3 - Stake-priming transaction:
0x4a66d01a017158ff38d6a88db98ba78435c606be57ca6df36033db4d9514f9f8 - Helper deployment transaction:
0xc53e6862a63887d717e0349c5dbd5c5faa333ac6deab13a68bb5b572df879d8c - Victim proxy:
0x34bd6dba456bc31c2b3393e499fa10bed32a9370 - Victim implementation:
0xb0d3586581cbbb41bbc0c74c0d36e607e56251c0 - EGD token:
0x202b233735bf743fa31abb8f71e641970161bf98 - USDT token:
0x55d398326f99059ff775485246999027b3197955 - EGD/USDT pair:
0xa361433e409adac1f87cdf133127585f8a93c67d - USDT/WBNB pair:
0x16b9a82891338f9ba80e2d6970fdda79d1eb0dae
Representative code and trace evidence:
implementation 0xb0d358...51c0: getEGDPrice() and claimAllReward() decompilation
exploit trace 0x50da...a2b3: calculateAll -> flash swaps -> claimAllReward -> Claim -> liquidation -> repayment
balance diff 0x50da...a2b3: attacker USDT delta and victim EGD delta