Platform: Code4rena
Start Date: 21/04/2022
Pot Size: $100,000 USDC
Total HM: 18
Participants: 60
Period: 7 days
Judge: gzeon
Total Solo HM: 10
Id: 112
League: ETH
Rank: 50/60
Findings: 2
Award: $128.95
🌟 Selected for report: 1
🚀 Solo Findings: 0
🌟 Selected for report: Dravee
Also found by: IllIllI, MaratCerby, UnusualTurtle, WatchPug, antonttc, berndartmueller, cccz, danb, horsefacts, hyh, pauliax, rayn, wuwe1
https://github.com/code-423n4/2022-04-backd/blob/main/backd/contracts/pool/EthPool.sol#L30
Same as https://github.com/code-423n4/2022-01-openleverage-findings/issues/75
For example, when user calls the redeem function of the EthPool contract to redeem native token, the whole user redeem is being handled with a payable.transfer() call.
This is unsafe as transfer has hard coded gas budget and can fail when the user is a smart contract.
Whenever the user either fails to implement the payable fallback function or cumulative gas cost of the function sequence invoked on a native token transfer exceeds 2300 gas consumption limit the native tokens sent end up undelivered and the corresponding user funds return functionality will fail each time.
And the same problem is found in withdraw function of VaultReserve contract
https://github.com/code-423n4/2022-04-backd/blob/main/backd/contracts/vault/VaultReserve.sol#L81
https://github.com/code-423n4/2022-04-backd/blob/main/backd/contracts/pool/EthPool.sol#L30 https://github.com/code-423n4/2022-04-backd/blob/main/backd/contracts/vault/VaultReserve.sol#L81 https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/
None
Using low-level call.value(amount) with the corresponding result check or using the OpenZeppelin Address.sendValue is advised:
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol#L60
#0 - chase-manning
2022-04-28T11:40:33Z
Duplicate of #52
🌟 Selected for report: cccz
Also found by: 0x1f8b, 0xDjango, 0xkatana, Dravee, IllIllI, WatchPug, berndartmueller, defsec, horsefacts, hyh, kenta, rayn, reassor, sorrynotsorry
58.8714 USDC - $58.87
On ChainlinkOracleProvider.sol and ChainlinkUsdWrapper.sol , we are using latestRoundData, but there is no check if the return value indicates stale data.
function _ethPrice() private view returns (int256) { (, int256 answer, , , ) = _ethOracle.latestRoundData(); return answer; } ... function getPriceUSD(address asset) public view override returns (uint256) { address feed = feeds[asset]; require(feed != address(0), Error.ASSET_NOT_SUPPORTED); (, int256 answer, , uint256 updatedAt, ) = AggregatorV2V3Interface(feed).latestRoundData(); require(block.timestamp <= updatedAt + stalePriceDelay, Error.STALE_PRICE); require(answer >= 0, Error.NEGATIVE_PRICE); uint256 price = uint256(answer); uint8 decimals = AggregatorV2V3Interface(feed).decimals(); return price.scaleFrom(decimals); }
This could lead to stale prices according to the Chainlink documentation:
https://docs.chain.link/docs/historical-price-data/#historical-rounds https://docs.chain.link/docs/faq/#how-can-i-check-if-the-answer-to-a-round-is-being-carried-over-from-a-previous-round
https://github.com/code-423n4/2022-04-backd/blob/main/backd/contracts/oracles/ChainlinkOracleProvider.sol#L55 https://github.com/code-423n4/2022-04-backd/blob/main/backd/contracts/oracles/ChainlinkUsdWrapper.sol#L64
None
function _ethPrice() private view returns (int256) { (uint80 roundID, int256 answer, , uint256 timestamp, uint80 answeredInRound) = _ethOracle.latestRoundData(); require(answeredInRound >= roundID, "Stale price"); require(timestamp != 0,"Round not complete"); require(answer > 0,"Chainlink answer reporting 0"); return answer; } ... function getPriceUSD(address asset) public view override returns (uint256) { address feed = feeds[asset]; require(feed != address(0), Error.ASSET_NOT_SUPPORTED); (uint80 roundID, int256 answer, , uint256 updatedAt, uint80 answeredInRound) = AggregatorV2V3Interface(feed).latestRoundData(); require(answeredInRound >= roundID, "Stale price"); require(answer > 0," Error.NEGATIVE_PRICE"); require(block.timestamp <= updatedAt + stalePriceDelay, Error.STALE_PRICE); uint256 price = uint256(answer); uint8 decimals = AggregatorV2V3Interface(feed).decimals(); return price.scaleFrom(decimals); }
#0 - chase-manning
2022-05-11T15:02:49Z
#1 - CerratoA
2023-05-06T20:23:48Z
the page is not found. how was this issue resolved?
#2 - chase-manning
2023-05-07T09:53:48Z
@CerratoA Sorry, that is a private repo which is why the link doesn't work. We made the code public but in a new repo. The final version of the contract that was deployed can be found here:
https://github.com/merofinance/protocol/blob/main/contracts/oracles/ChainlinkOracleProvider.sol