Platform: Code4rena
Start Date: 21/08/2023
Pot Size: $36,500 USDC
Total HM: 1
Participants: 43
Period: 7 days
Judge: Dravee
Id: 277
League: ETH
Rank: 28/43
Findings: 1
Award: $22.46
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: lsaudit
Also found by: 0x11singh99, 0x4non, 0xAnah, 0xSmartContract, 0xhacksmithh, JP_Courses, Jorgect, MrPotatoMagic, Sathish9098, epistkr, pfapostol, pontifex
22.4575 USDC - $22.46
4000 GAS
When the second call involves reading the same storage variable, it consumes more gas compared to caching it with a memory variable.
FILE: Breadcrumbs2023-08-shell/src/proteus/EvolvingProteus.sol 97: function p_min(Config storage self) public view returns (int128) { 98: + Config memory _self = self; - 99: if (t(self) > ABDK_ONE) return self.px_final; + 99: if (t(_self ) > ABDK_ONE) return _self.px_final; - 100: else return self.px_init.mul(ABDK_ONE.sub(t(self))).add(self.px_final.mul(t(self))); + 100: else return _self.px_init.mul(ABDK_ONE.sub(t(_self))).add(_self.px_final.mul(t(_self))); 101: } 106: function p_max(Config storage self) public view returns (int128) { + Config memory _self = self; - 107: if (t(self) > ABDK_ONE) return self.py_final; + 107: if (t(_self ) > ABDK_ONE) return _self.py_final; - 108: else return self.py_init.mul(ABDK_ONE.sub(t(self))).add(self.py_final.mul(t(self))); + 108: else return _self.py_init.mul(ABDK_ONE.sub(t(_self))).add(_self.py_final.mul(t(_self))); 109: }
Structs
for Reduced Slot Consumption
2000 GAS
The EVM works with 32 byte words. Variables less than 32 bytes can be declared next to eachother in storage and this will pack the values together into a single 32 byte storage slot (if the values combined are <= 32 bytes). If the variables packed together are retrieved together in functions we will effectively save ~2000 gas with every subsequent SLOAD for that storage slot. This is due to us incurring a Gwarmaccess (100 gas) versus a Gcoldsload (2100 gas)
t_init,t_final
can be used uin128 instead of uint256 : Saves 2000 GAS , 1 SLOT
The block.timestamp
in Solidity represents time in seconds since the Unix epoch (January 1, 1970)
. If we convert the maximum value that can be stored in a uint128 (2^128 - 1)
to years, we can get an estimate of the maximum representable time span.
the current estimated age of the universe is about 13.8 billion years
. The maximum uint128 timestamp value is astronomically larger than 13.8 billion years any practical application scenario you're likely to encounter.
FILE: 2023-08-shell/src/proteus/EvolvingProteus.sol 10: struct Config { 11: int128 py_init; 12: int128 px_init; 13: int128 py_final; 14: int128 px_final; - 15: uint256 t_init; - 16: uint256 t_final; + 15: uint128 t_init; + 16: uint128 t_final; 17: }
Due to how constant variables are implemented (replacements at compile-time), an expression assigned to a constant variable is recomputed each time that the variable is used, which wastes some gas. Saves 14 Gas
for every instances.
FILE: 2023-08-shell/src/proteus/EvolvingProteus.sol - 146: uint256 constant INT_MAX = uint256(type(int256).max); + 146: uint256 constant INT_MAX = 115792089237316195423570985008687907853269984665640564039457584007913129639935; - 151: int256 constant MIN_BALANCE = 10**12; + 151: int256 constant MIN_BALANCE = 1,000,000,000,000; - 181: uint256 constant MAX_BALANCE_AMOUNT_RATIO = 10**11; + 181: uint256 constant MAX_BALANCE_AMOUNT_RATIO = 100,000,000,000; - 191: uint256 constant FIXED_FEE = 10**9; + 191: uint256 constant FIXED_FEE = 1,000,000,000; - 201: int256 constant MAX_PRICE_RATIO = 10**4; + 201: int256 constant MAX_PRICE_RATIO = 10,000;
Consider caching the result instead of re-calling the function when possible. Note: this also includes casts, which cost between 42-46 gas, depending on the type.
FILE: 2023-08-shell/src/proteus/EvolvingProteus.sol + uint128 _tself = t(self) ; - 98: if (t(self) > ABDK_ONE) return self.px_final; + 98: if (_tself > ABDK_ONE) return self.px_final; - 99: else return self.px_init.mul(ABDK_ONE.sub(t(self))).add(self.px_final.mul(t(self))); + 99: else return self.px_init.mul(ABDK_ONE.sub(_tself)).add(self.px_final.mul(_tself)); 106: function p_max(Config storage self) public view returns (int128) { + uint128 _tself = t(self) ; - 107: if (t(self) > ABDK_ONE) return self.py_final; + 107: if (_tself > ABDK_ONE) return self.py_final; - 108: else return self.py_init.mul(ABDK_ONE.sub(t(self))).add(self.py_final.mul(t(self))); + 108: else return self.py_init.mul(ABDK_ONE.sub(_tself)).add(self.py_final.mul(_tself)); 109: }
public/external function names and public member variable names can be optimized to save gas. See this link for an example of how it works. Below are the interfaces/abstract contracts that can be optimized so that the most frequently-called functions use the least amount of gas possible during method lookup. Method IDs that have two leading zero bytes can save 128 gas each during deployment, and renaming functions to have lower method IDs will save 22 gas per call, per sorted position shifted
FILE: Breadcrumbs2023-08-shell/src/proteus/EvolvingProteus.sol ///@audit swapGivenInputAmount(),swapGivenOutputAmount(),depositGivenInputAmount(),depositGivenOutputAmount(),withdrawGivenOutputAmount(),withdrawGivenInputAmount(), 137: contract EvolvingProteus is ILiquidityPoolImplementation {
z = (x == y) ? true : false => z = (x == y)
FILE: Breadcrumbs2023-08-shell/src/proteus/EvolvingProteus.sol - 829: bool negative = amount < 0 ? true : false; + 829: bool negative = (amount < 0) ;
Ordering the members of a struct by size can help ensure that they are aligned to word boundaries as much as possible. By placing larger members before smaller members, it can reduce the amount of unused space within each 32-byte word and increase the likelihood that each member will be aligned to a word boundary.
FILE: Breadcrumbs2023-08-shell/src/proteus/EvolvingProteus.sol 10: struct Config { + 15: uint256 t_init; 11: int128 py_init; 12: int128 px_init; 13: int128 py_final; 14: int128 px_final; - 15: uint256 t_init; 16: uint256 t_final; 17: }
FILE: 2023-08-shell/src/proteus/EvolvingProteus.sol 146: uint256 constant INT_MAX = uint256(type(int256).max);
#0 - 0xRobocop
2023-08-30T01:30:34Z
Invalid, self needs to be passed as storage
Valid
Known - bot race
Valid
5-6 Known - bot race
Invalid
Known - bot race
#1 - c4-pre-sort
2023-08-30T01:31:03Z
0xRobocop marked the issue as sufficient quality report
#2 - c4-judge
2023-09-11T20:11:35Z
JustDravee marked the issue as grade-b