Summary
Lending markets are one of the core use cases of decentralized finance. Without them, we’d have to sell our favorite cryptos to ape into the newest 1000% APY farm. But thanks to protocols like Aave, Compound, Kashi, and Wild Credit, we can unlock the high APY potential of our favorite coins without sacrificing future gains.
Wild Credit is a money market protocol that quickly gained a lot of attention due to its eye-poppingly high APRs, often offering upwards of 25% APR for supplying ETH and 45%+ APR for supplying stablecoins (and making them available for borrowing), much higher than other protocols at the time.

The Ante team was initially made aware of Wild Credit due to an Ante Test written by resident Antean jseam. The high supply-side APR relative to the borrower interest paid made us question where the yield was coming from. Our investigation uncovered the answer — the utilization rate of the market was not being properly taken into account when calculating supplier interest accrued. This resulted in an accounting error that caused the protocol to owe more funds to its users than it actually contained.
At the time when the bug was discovered in late October, this deficit was increasing by over $100k/day. Though timely disclosure of the bug to the Wild Credit team prevented future losses, the deficit accrued by the protocol to that point already totaled around $14M.
Key takeaway— if something seems too good to be true, it probably is (even in crypto).
Vulnerability
Without loss of generality, let’s examine the DAI side of the DAI-ETH lending pair deployed here (now no longer in use): https://etherscan.io/address/0x3550e61886E1184B8BB732bb7135BDA9B7461801
Currently, DAI suppliers users accrue interest on their DAI balance every time the accrueAccount is called (when the user deposits or withdraws collateral and takes out or repays a loan). This is seen in the following code:
function accrueAccount(address _account) public {
_distributeReward(_account);
accrue();
_accrueAccountInterest(_account);
if (_account != feeRecipient()) {
_accrueAccountInterest(feeRecipient());
}
}
The accrue function called here calculates the cumulativeInterestRate paid by borrowers. This cumulativeInterestRate is then used to calculate the interest paid to suppliers in _accrueAccountInterest as shown below:
function _accrueAccountInterest(address _account) internal {
uint lpBalanceA = lpToken[tokenA].balanceOf(_account);
uint lpBalanceB = lpToken[tokenB].balanceOf(_account);
_accrueAccountSupply(tokenA, lpBalanceA, _account);
_accrueAccountSupply(tokenB, lpBalanceB, _account);
_accrueAccountDebt(tokenA, _account);
_accrueAccountDebt(tokenB, _account);
accountInterestSnapshot[tokenA][_account] = cumulativeInterestRate[tokenA];
accountInterestSnapshot[tokenB][_account] = cumulativeInterestRate[tokenB];
}
function _accrueAccountSupply(address _token, uint _amount, address _account) internal {
if (_amount > 0) {
uint supplyInterest = _newInterest(_amount, _token, _account);
uint newSupplyAccount = supplyInterest * _lpRate(_token) / 100e18;
uint newSupplySystem = supplyInterest * _systemRate(_token) / 100e18;
_mintSupply(_token, _account, newSupplyAccount);
_mintSupply(_token, feeRecipient(), newSupplySystem);
}
}
The _newInterest accrued to suppliers is calculated without considering the ratio of assets supplied to assets borrowed, resulting in an overestimate of the interest accrued:
function _newInterest(uint _balance, address _token, address _account) internal view returns(uint) {
uint currentCumulativeRate = cumulativeInterestRate[_token] + _pendingInterestRate(_token);
return _balance * (currentCumulativeRate - accountInterestSnapshot[_token][_account]) / 100e18;
}
The correct calculation would change the return statement in _newInterest as described in the following pseudocode:
return _balance * (debt/supply) * (currentCumulativeRate - accountInterestSnapshot[_token][_account]) / 100e18;
Writing bug-free code is hard, and the stakes are high on-chain. Here, a one-line bug caused an 8 figure loss in user funds!
NB: _lpRate and _systemRate are both set to 50% and do not affect the proof of concept
Example
Assume the borrow rate for the ETH-DAI market is 94.7% and 1000 DAI are supplied to the market, 200 DAI borrowed.
The APR paid to suppliers is set at 50% of the borrow rate (the _lpRate above).
Thus while borrowers are paying an interest of 189 DAI per year (0.947 * 200), suppliers are “earning” 473 DAI per year (0.473 * 1000), resulting in a net deficit to the protocol of 284 DAI per year. Repeat this calculation using the real parameters and across all lending markets to calculate the full impact of the bug.
Fix
The Wild Credit team moved swiftly to mitigate further losses from the protocol by (1) pausing all borrowing/supplying in the contracts and (2) moving all funds in their lending markets to the protocol treasury.
Following this, the team calculated their users’ total supplied balances net of their borrowed balances and compared this quantity to the funds present in the protocol. They determined that the total deficit amounted to 22% of user funds and this loss was socialized.
Users were refunded proportionally from the protocol assets and issued wBOND tokens equal to the deficit amount. These wBOND tokens are set to mature at $1 with no fixed expiration, and will be paid off using future protocol revenue.
Acknowledgments
We’d like to acknowledge Wild Credit for their swift and effective harm mitigation and commitment to making their users whole. We’d also like to thank Immunefi for their guidance and support during the disclosure process. Additionally, we’d like to acknowledge members of Cluster Capital for discussions which led to the discovery of the issue.
Finally, we’d like to thank our community member jseam for writing an Ante Test for Wild Credit and bringing the protocol to our attention. As part of this post-mortem and as a show of support for the Wild Credit team going forward, the Ante team will be staking our portion of the bounty payout (~2 ETH) in jseam’s Ante Test, which assesses the integrity of Wild Credit’s price oracles.
To find out more about how Ante Tests enable monetary piece of mind in DeFi and connect with other security-conscious degens, follow us on Twitter and join our Discord.
