Platform: Code4rena
Start Date: 09/09/2022
Pot Size: $42,000 USDC
Total HM: 2
Participants: 101
Period: 3 days
Judge: hickuphh3
Total Solo HM: 2
Id: 161
League: ETH
Rank: 3/101
Findings: 1
Award: $328.66
🌟 Selected for report: 1
🚀 Solo Findings: 0
🌟 Selected for report: GalloDaSballo
Also found by: 0x040, 0x1f8b, 0x4non, 0x52, 0x85102, 0xNazgul, 0xSky, 0xSmartContract, Aymen0909, Bnke0x0, CertoraInc, Chandr, Chom, CodingNameKiki, Deivitto, Diana, Funen, JC, Jeiwan, Junnon, KIntern_NA, Lambda, Mohandes, Noah3o6, Ocean_Sky, Picodes, R2, Randyyy, RaymondFam, ReyAdmirado, Rohan16, Rolezn, Samatak, Sm4rty, SnowMan, SooYa, StevenL, Tagir2003, Tointer, TomJ, Tomo, V_B, Waze, _Adam, __141345__, a12jmx, ajtra, ak1, asutorufos, bharg4v, bobirichman, brgltd, c3phas, cccz, cryptonue, cryptostellar5, cryptphi, csanuragjain, d3e4, datapunk, delfin454000, dipp, djxploit, durianSausage, erictee, fatherOfBlocks, gogo, got_targ, hansfriese, horsefacts, hyh, ignacio, innertia, izhuer, karanctf, ladboy233, leosathya, lucacez, lukris02, mics, oyc_109, pashov, pauliax, prasantgupta52, rbserver, ret2basic, rfa, robee, rokinot, rotcivegaf, rvierdiiev, sach1r0, scaraven, sikorico, simon135, smiling_heretic, sorrynotsorry, unforgiven, wagmi, yixxas
328.6554 USDC - $328.66
Personally haven't found any glaring vulnerability.
Because of the mix of immutability and Admin Trust, end users should review the deployment settings at that time to ensure that no misconfigurations have happened.
In case any misconfigurations happens, a new deployment will be required.
Listed below are some observations of things that could be refactored to make the code more consistent, as well as some gotchas that are introduced by the choice of architecture
hasNotSigned
signAndClaimAndRedeem
has the modifier while signAndClaim
doesn't
function signAndClaimAndRedeem( bytes calldata signature, address[] calldata cTokens, uint256[] calldata amountsToClaim, uint256[] calldata amountsToRedeem, bytes32[][] calldata merkleProofs ) external override hasNotSigned nonReentrant { _sign(signature); _multiClaim(cTokens, amountsToClaim, merkleProofs); _multiRedeem(cTokens, amountsToRedeem); }
Doesn't have the modifier https://github.com/code-423n4/2022-09-tribe/blob/769b0586b4975270b669d7d1581aa5672d6999d5/contracts/shutdown/fuse/RariMerkleRedeemer.sol#L88-L97
function signAndClaim( bytes calldata signature, address[] calldata cTokens, uint256[] calldata amounts, bytes32[][] calldata merkleProofs ) external override nonReentrant { // both sign and claim/multiclaim will revert on invalid signatures/proofs _sign(signature); _multiClaim(cTokens, amounts, merkleProofs); }
Add the modifier hasNotSigned
for consistency, or remove the modifier altogether and allow to sign multiple times the same message
For the in-scope code MESSAGE
is left to the default value
string public constant MESSAGE = "Sample message, please update.";
This could cause the signature to be replayable in other applications that use the same message.
Add the proper message, most likely a TOS acknowledgement or a ipfs hash to a document
amountToDrip * 2 - 1
While the code is meant to limit the total asset at risk, it's important to notice that because of the following check:
IERC20(token).balanceOf(target) < amountToDrip,
Any balance below amountToDrip
, after enough time has passed, will alow to drip more, meaning the total amount at risk is not amountToDrip
but up to 2 * amountToDrip - 1
I recommend commenting this
Because amountOut = amountFeiIn;
and amountFeiOut = amountIn;
there is no slippage, and no risk for any front-run
The check below is always true for that reason:
require(amountOut >= minAmountOut, "SimpleFeiDaiPSM: Redeem not enough out");
A better require would be to check if the balance of the token that will be transferred out is sufficient to avoid a revert on the low level balance subtraction
redeemBase = _redeemBase;
Because redeemedToken
is known, you could just retrieve the totalSupply
from it to ensure the claims are for all tokens available.
End users will have to verify that redeemBase
is consistent with the Circulating Supply.
Comment and let end users know of this, or use totalSupply
from the token
Because TribeRedeemer assumes all assets will have the same decimals, tokens can remain stuck if they have a different amount of decimals.
A simple check for decimals in the constructor can avoid this scenario https://github.com/code-423n4/2022-09-tribe/blob/769b0586b4975270b669d7d1581aa5672d6999d5/contracts/shutdown/redeem/TribeRedeemer.sol#L33-L34
tokensReceived = _tokensReceived;
In lack of a check, end users will have to verify all tokens have 18 decimals.
All of the tokens in the README have 18 decimals at this time.
Because redeem
doesn't burn FEI, any caller can mint
and redeem
multiple times in the same tx with the goal of arbing out the FEI - DAI pair
function burnFeiHeld() external {
While FEI being tradeable for DAI is enforcing a 1-1 trade (FEI price goes up due to arbing, up to 100% + FEE), allowing the opposite swap is a easy target for arbitrageurs
Constructors don't have zero-checks, which could force a re-deployment, funds are not at risk in those cases