Platform: Code4rena
Start Date: 16/12/2021
Pot Size: $100,000 USDC
Total HM: 21
Participants: 25
Period: 7 days
Judge: alcueca
Total Solo HM: 12
Id: 66
League: ETH
Rank: 17/25
Findings: 2
Award: $718.60
π Selected for report: 1
π Solo Findings: 0
π Selected for report: defsec
Also found by: 0x1f8b, Jujic, WatchPug, broccolirob, certora, cmichel, csanuragjain, hyh, jayjonah8, kenzo, robee, sirhashalot
11.5426 USDC - $11.54
certora
https://github.com/code-423n4/2021-12-yetifinance/blob/main/packages/contracts/contracts/StabilityPool.sol#L947 use of transfer instead of safeTransfer
change to safeTransfer
#0 - kingyetifinance
2022-01-07T08:37:08Z
@LilYeti : This issue was referenced many times and generalized to anything related to safe transfer of erc20's and checking return values.
#1 - kingyetifinance
2022-01-10T09:01:12Z
@LilYeti : Solved #1, #27, #59, #94, #101, #114, #118, #130, #131, #134, #196, #207, #227, #229, #232, #239, #241, #267, #298, #301
#2 - alcueca
2022-01-15T07:26:57Z
Duplicate of #94
certora
You have many loops in your code.
In all those loop you use postfix opertaions, i++
by replacing to prefix operations,++i
you can save a little bit of gas every iteration.
This amount of gas will add up very quickly since there are a lot of loops and each loop iterate many times.
This is an easy fix and can save a lot of gas.
++i
is cheaper than i++
because it doesnt save the previous value of i
to return it later
Manual code review
replace every i++
with ++i
where it doesnt affect the logic (of course also i--
to --i
)
#0 - kingyetifinance
2022-01-06T09:10:23Z
Duplicate #12
π Selected for report: certora
531.2947 USDC - $531.29
certora
In StabilityPool
line 747 you multiply a sum of two parameters but since the second parameter is calculated through division it can cause lack of precision since the order of the operations is div()->mul() instead of mul()->div()
Manual code review
dont divide secondPortion
by SCALE_FACTOR
, instead multiply the firstPortion
by SCALE_FACTOR
and after you multiply their sum by initialDeposit
, divide the result by SCALE_FACTOR
.
like this:
original:
uint256 firstPortion = epochToScaleToSum[asset][snapshots.epoch][snapshots.scale].sub( S_Snapshot ); uint256 secondPortion = epochToScaleToSum[asset][snapshots.epoch][snapshots.scale.add(1)] .div(SCALE_FACTOR); uint256 assetGain = initialDeposit.mul(firstPortion.add(secondPortion)).div(P_Snapshot).div( DECIMAL_PRECISION );
changed:
uint256 firstPortion = epochToScaleToSum[asset][snapshots.epoch][snapshots.scale].sub( S_Snapshot ).mul(SCALE_FACTOR); uint256 secondPortion = epochToScaleToSum[asset][snapshots.epoch][snapshots.scale.add(1)]; uint256 assetGain = initialDeposit.mul(firstPortion.add(secondPortion)).div(SCALE_FACTOR).div(P_Snapshot).div( DECIMAL_PRECISION );
#0 - kingyetifinance
2022-01-05T17:20:46Z
@LilYeti: This is intended behavior, and acts the same in Liquity. https://github.com/liquity/dev/blob/main/packages/contracts/contracts/StabilityPool.sol#L661-L678
#1 - alcueca
2022-01-16T06:53:12Z
The sponsor acknowledged the issue, since it is not stated how the loss of precision is better than the alternative.
26.3494 USDC - $26.35
certora
In multiple functions you use named return values but still use the return statment.
For example in _getTotalVariableDepositFee
in BorrowerOperations
in getPendingYUSDDebtReward
in TroberManager
and in many more...
This is very misleading (beside a waste of gas) and cause a confusing documantation
Manual code review
remove the return statement or the named return value
#0 - kingyetifinance
2022-01-05T07:14:05Z
@LilYeti: Should be severity 0 or Gas since it is readability / gas optimization.
#1 - 0xtruco
2022-01-13T23:54:15Z
Resolved gas issue in https://github.com/code-423n4/2021-12-yetifinance/pull/25
#2 - alcueca
2022-01-15T16:53:43Z
Duplicate of #24
π Selected for report: Dravee
Also found by: WatchPug, certora, sirhashalot
17.7859 USDC - $17.79
certora
https://github.com/code-423n4/2021-12-yetifinance/blob/main/packages/contracts/contracts/TroveManager.sol#L672 https://github.com/code-423n4/2021-12-yetifinance/blob/main/packages/contracts/contracts/YUSDToken.sol#L255
the following lines use assert to validate user input, it's wrong and should be replaced with require. here is an article that explains why you should always use require to validate user input: https://medium.com/blockchannel/the-use-of-revert-assert-and-require-in-solidity-and-the-new-revert-opcode-in-the-evm-1a3a7990e06e
users who accidentally provide invalid input will not be refunded by the remaining gas, and it can be a very high amount.
replace assert
with require
in the lines I wrote.
#0 - kingyetifinance
2022-01-02T21:39:17Z
@LilYeti: Is a gas optimization, should be Gas severity. Doesn't cause loss of user funds in the protocol like in description of medium error.
#1 - kingyetifinance
2022-01-10T08:56:09Z
#2 - kingyetifinance
2022-01-10T09:59:09Z
Resolved #280 #297 #157 #194 https://github.com/code-423n4/2021-12-yetifinance/pull/8
#3 - alcueca
2022-01-15T06:58:38Z
Duplicate #157
π Selected for report: Dravee
Also found by: WatchPug, certora, sirhashalot
17.7859 USDC - $17.79
certora
In CommuintyIssuance.sol
line 84 you use assert instead of require.
assert is a waste of gas since it will take all the gas that was sent to the transaction instead of stopping at the amount of gas spent until the failure.
Manual code review
replace assert with require
#0 - kingyetifinance
2022-01-06T09:06:09Z
@LilYeti: Duplicate #51 and that issue should level severity G
#1 - alcueca
2022-01-15T06:58:52Z
Duplicate #157
17.7859 USDC - $17.79
certora
https://github.com/code-423n4/2021-12-yetifinance/blob/main/packages/contracts/contracts/YETI/sYETIToken.sol#L231 wrong use of deadline field:
uint256[] memory amounts = IRouter(routerAddress).swapExactTokensForTokens(YUSDToSell, YETIOutMin, path, address(this), block.timestamp + 5 minutes);
the deadline is the timestamp after which the transaction will revert.
the goal of this field is that the caller can set a deadline for the transaction so the transaction will not succeed in any arbitrary time in the future, and after this deadline, they can resubmit the transaction:
https://help.uniswap.org/en/articles/5455497-my-transaction-has-been-pending-for-a-long-time-what-can-i-do#:~:text=Wait%20%E2%80%94%20the%20Uniswap%20interface%20has,you%20can%20resubmit%20your%20transaction.
here it is set to block.timestamp + 5 minutes
.
the problem here is that the transaction will always occur at block.timestamp
so setting the deadline to be block.timestamp + 5 minutes
won't have any effect.
therefore the transaction can still run in an arbitrary time in the future.
block.timestamp
is confused with the time the transaction was submitted.
practically, buyBack
transaction doesn't have a deadline
The owner calls buyBack
, they have read the code so they expect that it won't take more than 5 minutes since they submitted the transaction.
However, it can take an arbitrary time for the transaction to succeed.
If it takes too long, the owner can try to cancel it in order to resubmit another, but it's not guaranteed to work:
https://help.uniswap.org/en/articles/5455497-my-transaction-has-been-pending-for-a-long-time-what-can-i-do#:~:text=Wait%20%E2%80%94%20the%20Uniswap%20interface%20has,you%20can%20resubmit%20your%20transaction
If it won't work, then the owner won't be able to resubmit it, and it can take a very long time until the transaction is done.
manual review
add a parameter in buyBack
for the swap deadline.
#0 - kingyetifinance
2022-01-06T07:50:20Z
@LilYeti: Is not really a problem and is a gas optimization in reality. Since there are more operations to complete after and the tx has to be confirmed to go through / balance is checked, it has to be in that block.
#1 - 0xtruco
2022-01-11T07:15:47Z
Also fixed #92, #211, #269
#2 - alcueca
2022-01-15T17:07:04Z
Duplicate #211
certora
In multiple for loops you access the .length
if an array in every loop iteration.
for example in BorrowerOperations line 873, 920, 929,930 and more...
This is a waste of gas and could easily be reduced by saving the length in a local variable
Checked on Remix
Manual code review
Save the length in a local variable right before entering the loop and iterate over it instead
#0 - kingyetifinance
2022-01-06T07:55:15Z
@LilYeti: Duplicate of #14
#1 - alcueca
2022-01-15T07:17:34Z
Duplicate #283
96.8285 USDC - $96.83
certora
Most of the contracts use solidity version 0.6.11
some use 0.6.12 like sYETIToken.sol
and BoringBatchable.sol
and more..
some even use 0.8.7 like ERC20_8.sol
This may cause a problem
Manual code review
change all contract to the same solidity version, if you lower some to before 0.8.0 make sure you change to safeMath too if needed
#0 - kingyetifinance
2022-01-05T06:15:27Z
@LilYeti Duplicate #21
#1 - alcueca
2022-01-15T07:04:35Z
Duplicate #158