Platform: Code4rena
Start Date: 04/03/2024
Pot Size: $36,500 USDC
Total HM: 9
Participants: 80
Period: 7 days
Judge: hansfriese
Total Solo HM: 2
Id: 332
League: ETH
Rank: 33/80
Findings: 2
Award: $51.12
š Selected for report: 0
š Solo Findings: 0
š Selected for report: slvDev
Also found by: 0x11singh99, 0xhacksmithh, SAQ, SY_S, albahaca, dharma09, hunter_w3b, shamsulhaq123, unique
19.3678 USDC - $19.37
no | Issue | Instances | |
---|---|---|---|
[G-01] | Use assembly to emit events | 13 | - |
[G-02] | Use hardcode address instead address(this) | 15 | - |
[G-03] | Using unchecked blocks to save gasUsing unchecked blocks to save gas | 3 | - |
[G-04] | Use assembly for math (add, sub, mul, div) | 3 | - |
[G-05] | Not using the named return variable when a function returns, wastes deployment gas | 1 | - |
[G-06] | Use assembly for small keccak256 hashes, in order to save gas | 1 | - |
[G-07] | Use assembly to validate msg.sender | 1 | - |
[G-08] | Use Mappings Instead of Arrays | 1 | - |
[G-09] | Sort Solidity operations using short-circuit mode | 1 | - |
[G-10] | Before transfer of some functions, we should check some variables for possible gas save | 2 | - |
[G-11] | Use assembly to check for address(0) | 3 | - |
[G-12] | UsingĀ PRIVATEĀ rather thanĀ PUBLICĀ FOR Immutable, Saves Gas | 2 | - |
[G-13] | Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead | 2 | - |
We can use assembly to emit events efficiently by utilizingĀ scratch spaceĀ and theĀ free memory pointer. This will allow us to potentially avoid memory expansion costs. Note: In order to do this optimization safely, we will need to cache and restore the free memory pointer.
file:/src/PrizeVault.sol 562 emit Sponsor(_owner, _assets, _shares); 621 emit ClaimYieldFeeShares(msg.sender, _shares); 697 emit TransferYieldOut(msg.sender, _tokenOut, _receiver, _amountOut, _yieldFee); 747 emit LiquidationPairSet(address(this), address(_liquidationPair)); 876 emit Deposit(_caller, _receiver, _assets, _shares); 909 emit Withdraw(_caller, _receiver, _owner, _assets, _shares); 952 emit YieldFeePercentageSet(_yieldFeePercentage); 960 emit YieldFeeRecipientSet(_yieldFeeRecipient);
file:/src/TwabERC20.sol 78 emit Transfer(address(0), _receiver, _amount); 89 emit Transfer(_owner, address(0), _amount); 102 emit Transfer(_from, _to, _amount);
file:/src/abstract/Claimable.sol 131 emit ClaimerSet(_claimer);
file:/src/abstract/HookManager.sol 31 emit SetHooks(msg.sender, hooks);
address(this)
Instead of usingĀ address(this), it is more gas-efficient to pre-calculate and use the hardcodedĀ address. Foundryās script.sol and solmateāsĀ LibRlp.solĀ contracts can help achieve this.
file:/src/PrizeVault.sol 337 return yieldVault.convertToAssets(yieldVault.balanceOf(address(this))) + _asset.balanceOf(address (this)); 382 uint256 _latentBalance = _asset.balanceOf(address(this)); 383 uint256 _maxYieldVaultDeposit = yieldVault.maxDeposit(address(this)); 405 uint256 _maxWithdraw = _maxYieldVaultWithdraw() + _asset.balanceOf(address(this)); 416 uint256 _maxWithdraw = _maxYieldVaultWithdraw() + _asset.balanceOf(address(this)); 540 IERC20Permit(address(_asset)).permit(_owner, address(this), _assets, _deadline, _v, _r, _s); 558 if (twabController.delegateOf(address(this), _owner) != SPONSORSHIP_ADDRESS) { 639 _maxAmountOut = _maxYieldVaultWithdraw() + _asset.balanceOf(address(this)); 726 return (_tokenOut == address(_asset) || _tokenOut == address(this)) && _liquidationPair == liquidationPair; 861 uint256 _assetsWithDust = _asset.balanceOf(address(this)); 866 uint256 _assetsUsed = yieldVault.mint(_yieldVaultShares, address(this)); 922 return yieldVault.convertToAssets(yieldVault.maxRedeem(address(this))); 936 yieldVault.redeem(_yieldVaultShares, address(this), address(this));
file:/src/TwabERC20.sol 59 return twabController.balanceOf(address(this), _account); 64 return twabController.totalSupply(address(this));
Solidity version 0.8+ comes with implicit overflow and underflow checks on unsigned integers. When an overflow or an underflow isn't possible (as an example, when a comparison is made before the arithmetic operation), some gas can be saved by using an unchecked block.
file:/src/PrizeVault.sol 617 yieldFeeBalance -= _yieldFeeBalance; 647 uint256 _liquidYield = 648 _availableYieldBalance(totalAssets(), _totalDebt(_totalSupply)) 649 .mulDiv(FEE_PRECISION - yieldFeePercentage, FEE_PRECISION); 675 _yieldFee = (_amountOut * FEE_PRECISION) / (FEE_PRECISION - _yieldFeePercentage) - _amountOut;
Use assembly for math instead of Solidity. You can check for overflow/underflow in assembly to ensure safety. If using Solidity versions < 0.8.0 and you are using Safemath, you can gain significant gas savings by using assembly to calculate values and checking for overflow/underflow.
file:/src/PrizeVault.sol 416 uint256 _maxWithdraw = _maxYieldVaultWithdraw() + _asset.balanceOf(address(this)); 617 yieldFeeBalance -= _yieldFeeBalance; 639 _maxAmountOut = _maxYieldVaultWithdraw() + _asset.balanceOf(address(this));
When you execute a function that returns values in Solidity, the EVM still performs the necessary operations to execute and return those values. This includes the cost of allocating memory and packing the return values. If the returned values are not utilized, it can be seen as wasteful since you are incurring gas costs for operations that have no effect.
file:/src/PrizeVaultFactory.sol ) external returns (PrizeVault) {
If the arguments to the encode call can fit into the scratch space (two words or fewer), then it's more efficient to use assembly to generate the hash (80 gas)
File: /src/PrizeVaultFactory.sol 103 salt: keccak256(abi.encode(msg.sender, deployerNonces[msg.sender]++))
msg.sender
file:/src/abstract/Claimable.sol 53 if (msg.sender != claimer) revert CallerNotClaimer(msg.sender, claimer);
When we say "use mappings instead of arrays," we mean that when storing and accessing data in a smart contract, it's more gas-efficient to use mappings instead of arrays. This is because mappings do not require iteration to find a specific element, whereas arrays do.
file:/src/PrizeVaultFactory.so 66 PrizeVault[] public allVaults;
file:/src/PrizeVault.sol 776 if (success && encodedDecimals.length >= 32) {
Before transfer, we should check for amount being 0 so the function doesn't run when its not gonna do anything:
file:/src/PrizeVault.sol 939 _asset.transfer(_receiver, _assets);
file:/src/TwabERC20.sol 78 emit Transfer(address(0), _receiver, _amount);
address(0)
file: /src/PrizeVault.sol 300 if (address(yieldVault_) == address(0)) revert YieldVaultZeroAddress(); 301 if (owner_ == address(0)) revert OwnerZeroAddress(); 743 if (address(_liquidationPair) == address(0)) revert LPZeroAddress();
If needed, the value can be read from the verified contract source code. Savings are due to the compiler not having to create non-payable getter functions for deployment calldata, and not adding another entry to the method ID table
file: /src/TwabERC20.sol 26 TwabController public immutable twabController;
file:/src/abstract/Claimable.sol 24 PrizePool public immutable prizePool;
When using elements that are smaller than 32 bytes, your contract's gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size.
file:/src/TwabERC20.sol 101 twabController.transfer(_from, _to, SafeCast.toUint96(_amount));
File:/src/abstract/Claimable.sol 21 uint24 public constant HOOK_GAS = 150_000;
#0 - raymondfam
2024-03-13T03:44:25Z
13 generic G
#1 - c4-pre-sort
2024-03-13T03:44:28Z
raymondfam marked the issue as sufficient quality report
#2 - c4-judge
2024-03-18T02:56:22Z
hansfriese marked the issue as grade-b
#3 - hansfriese
2024-03-21T04:56:12Z
Some findings are known issues. I advise omitting such findings before submit.
31.7525 USDC - $31.75
no | File |
---|---|
[File-1] | PrizeVault.sol |
[File-2] | PrizeVaultFactory.sol |
[File-3] | TwabERC20.sol |
[File-4] | Claimable.sol |
[File-5] | HookManager.sol |
Permit Functionality: The depositWithPermit
function allows users to approve and deposit assets using a permit. However, the comment mentions a potential risk related to the lack of a receiver parameter in the permit, allowing potential misuse.
Access Controls: The contract includes several functions with the onlyOwner
modifier, indicating that certain actions can only be performed by the owner. Ensure that proper access controls are in place and appropriately tested
TWAB Controller Dependency: The contract relies on an external TWAB controller (twabController
). Ensure that the integration with this controller is secure and well-audited, as it seems to impact critical functions like sponsorship.
Yield Fee Handling: The contract implements a yield fee mechanism. Ensure that the fee calculations and distributions are well-tested to prevent any unintended consequences.
ERC-20 Approval Risks: The contract uses ERC-20's approve
function, and it is crucial to handle allowances securely to prevent potential vulnerabilities like front-running attacks.
Reentrancy Consideration: The code mentions considerations related to reentrancy during asset transfers. Make sure that reentrancy risks are thoroughly mitigated, especially during ERC-777 and ERC-20 transfers.
External Contracts: The contract interacts with external contracts, such as yieldVault
and prizePool
. Ensure that these contracts are trustworthy and correctly integrated, considering potential vulnerabilities.
Liquidation Logic: The ILiquidationSource
interface and liquidation-related functions introduce dependencies on external systems. Assess and test the integration with these systems to avoid unforeseen issues.
ERC-777 Considerations: The contract mentions handling ERC-777 tokens and notes potential reentrancy issues. Ensure the contract correctly handles ERC-777 tokens, considering their unique features.
Permit Functionality: The use of permit functionality may be non-standard, depending on the specific ERC-20 implementation. Ensure compatibility with popular ERC-20 token standards.
Nonce Handling: The contract utilizes nonces for address-based salts during vault deployment (keccak256(abi.encode(msg.sender, deployerNonces[msg.sender]++))
). While nonces generally prevent replay attacks, ensure that the nonce management is secure and doesn't introduce vulnerabilities.
Token Transfer: The deployVault
function transfers assets to the newly deployed vault to fill the yield buffer. Admins must be cautious about the management of these funds, and any vulnerability in this process could lead to potential abuse.
Dependency on External Contracts: The contract relies on external contracts (PrizeVault
, IERC4626
, PrizePool
) for various functionalities. Ensure that these contracts are secure and well-audited, as any vulnerabilities in them could impact the overall system.
Relying on External Yield Vault: The contract relies on an external ERC4626 yield vault (_yieldVault
). Ensure that the integration with this yield vault is secure, and consider potential risks associated with the yield generation process.
CREATE2 Usage: The contract uses CREATE2
to deploy new PrizeVault instances. While this is a standard practice, ensure that the implementation is correct, and the risk of collisions is adequately mitigated.
Address Calculation for Salt: The salt for CREATE2
is generated using keccak256(abi.encode(msg.sender, deployerNonces[msg.sender]++))
. Validate that this salt calculation is robust and doesn't introduce any vulnerabilities.
ERC-4626 Integration: The contract relies on an external ERC-4626 yield vault (_yieldVault
). Ensure proper integration with this contract and assess potential risks associated with the yield generation process.
PrizePool Integration: The contract interacts with an external PrizePool
contract (_prizePool
). Verify that the integration is secure and well-tested, considering potential implications for prize computations.
deployVault
function involves the transfer of tokens (IERC20(_vault.asset()).transferFrom(msg.sender, address(_vault), YIELD_BUFFER)
). Ensure that this token transfer is secure and won't result in any unexpected behavior.Yield Fee Handling: The contract includes parameters related to yield fees (_yieldFeeRecipient
, _yieldFeePercentage
). Assess the implementation to ensure correct handling of yield fees and prevent any unintended consequences.
Security Audits:
PrizeVault
, IERC4626
, PrizePool
).Nonce Security:
Token Transfers:
deployVault
function for robustness.Yield Vault Integration:
CREATE2 Usage:
TwabController
. Systemic risks may arise if the TwabController has vulnerabilities or malfunctions. Thoroughly audit the TwabController for robustness.uint96
for balances. Review potential gas implications and ensure minting doesn't exceed uint96 limits, causing failed transactions.TwabController
). Ensure compatibility with other systems, and assess risks associated with potential changes or upgrades to the TwabController.TwabController Audit:
Gas Efficiency Testing:
uint96
limits.Integration Testing:
Documentation:
Security Best Practices:
claimer
variable, which determines the address allowed to claim prizes. Ensure that the
address is set securely and that its authority is well-defined.PrizePool
contract. Systemic risks may arise if the PrizePool has vulnerabilities or malfunctions. A comprehensive audit of the PrizePool contract is crucial.claimPrize
function interacts with external systems, specifically the PrizePool. Integration risks may arise if the PrizePool contract behavior changes. Test thoroughly for seamless interaction.claimer
address, potentially utilizing multi-signature schemes or time-lock mechanisms to minimize admin abuse risks.PrizePool
contract to identify and address any vulnerabilities. Ensure that the PrizePool contract is robust and secure.claimer
address and the hook mechanisms.setHooks
function allows any account to set hooks for themselves. Ensure that only authorized users can set hooks to prevent misuse.setHooks
function. This helps prevent unauthorized users from setting hooks.5 hours
#0 - c4-pre-sort
2024-03-13T03:10:08Z
raymondfam marked the issue as sufficient quality report
#1 - c4-judge
2024-03-18T04:31:22Z
hansfriese marked the issue as grade-b