Platform: Code4rena
Start Date: 27/01/2022
Pot Size: $90,000 USDC
Total HM: 21
Participants: 33
Period: 7 days
Judge: Jack the Pug
Total Solo HM: 14
Id: 78
League: ETH
Rank: 6/33
Findings: 3
Award: $3,254.43
🌟 Selected for report: 7
🚀 Solo Findings: 1
🌟 Selected for report: Dravee
1987.8596 USDC - $1,987.86
Dravee
Wrong fateBalance bookkeeping for a user. Wrong fateCreated value emitted.
Taking into account the FOT is done almost everywhere important in the solution already. That's a known practice in the solution.
However, it's missing here (see @audit-info tags):
File: LimboDAO.sol 383: function burnAsset(address asset, uint256 amount) public isLive incrementFate { 384: require(assetApproved[asset], "LimboDAO: illegal asset"); 385: address sender = _msgSender(); 386: require(ERC677(asset).transferFrom(sender, address(this), amount), "LimboDAO: transferFailed"); //@audit-info FOT not taken into account 387: uint256 fateCreated = fateState[_msgSender()].fateBalance; 388: if (asset == domainConfig.eye) { 389: fateCreated = amount * 10; //@audit-info wrong amount due to lack of FOT calculation 390: ERC677(domainConfig.eye).burn(amount);//@audit-info wrong amount due to lack of FOT calculation 391: } else { 392: uint256 actualEyeBalance = IERC20(domainConfig.eye).balanceOf(asset); 393: require(actualEyeBalance > 0, "LimboDAO: No EYE"); 394: uint256 totalSupply = IERC20(asset).totalSupply(); 395: uint256 eyePerUnit = (actualEyeBalance * ONE) / totalSupply; 396: uint256 impliedEye = (eyePerUnit * amount) / ONE;//@audit-info wrong amount due to lack of FOT calculation 397: fateCreated = impliedEye * 20; 398: } 399: fateState[_msgSender()].fateBalance += fateCreated; //@audit-info potentially wrong fateCreated as fateCreated can be equal to amount * 10; 400: emit assetBurnt(_msgSender(), asset, fateCreated);//@audit-info potentially wrong fateCreated emitted 401: }
VS Code
Check the balance before and after the transfer to take into account the Fees-On-Transfer
#0 - gititGoro
2022-02-05T02:08:19Z
Nice catch! It's not a level 3 bug, though.
#1 - jack-the-pug
2022-02-27T07:26:10Z
Downgrade to Med
as the assets need to be whitelisted.
#2 - gititGoro
2022-06-25T01:27:33Z
setEYEBasedAssetStake cannot handle FOT tokens predictably because of the invariant checks on initial balance and final balance. Adding an amount parameter to circumvent this undermines the security of checking invariants. So no PR will be opened against this issue. In retrospect I should have marked it invalid. Marking resolved.
Dravee
Increased gas cost (possible DOS)
Here's the parameterize
function:
function parameterize( address token, uint256 crossingThreshold, uint256 soulType, uint256 state, uint256 index, uint256 targetAPY, uint256 daiThreshold ) public notCurrent { require(morgothApprover.approved(token), "MORGOTH: token not approved for listing on Behodler"); params.push( Parameters({ token: token, crossingThreshold: crossingThreshold, soulType: soulType, state: state, index: index, targetAPY: targetAPY, daiThreshold: daiThreshold }) ); }
As we can see, as long as the token address has been approved, the parameters can be pushed multiple times in the array Parameters[] params
. Also, there's no access control capabilities here.
Furthermore, the state variable Parameters[] params
is used in a loop reading from storage and making external calls on all values:
function execute() internal override returns (bool) { for (uint256 i = 0; i < params.length; i++) { uint256 fps = ammHelper.minAPY_to_FPS(params[i].targetAPY, params[i].daiThreshold); limbo.configureSoul( params[i].token, params[i].crossingThreshold, params[i].soulType, params[i].state, params[i].index, fps ); } return true; }
While this will indeed update existing tokens, but it will first update them in every previous state they were in in the past. Having duplicates is not a good idea for gas cost and DOS potential here.
VS Code
Consider making an existence check on the token
parameter in the params
storage variable and only push in the array if it's not found. Otherwise: edit the existing configuration values in params
corresponding to the existing token
#0 - gititGoro
2022-02-10T11:59:38Z
duplicate of issue 26
#1 - jack-the-pug
2022-02-22T15:06:34Z
Dup #26
Dravee
Silent failures (lack of failure detection / revert in case of failure).
ERC20 transfer()/transferFrom() do not return booleans: Contracts compiled with solc >= 0.4.22 interacting with such functions will revert. Use OpenZeppelin's SafeERC20 (wrappers around ERC20 operations that throw on failure when the token contract implementation returns false. Tokens that return no value and instead revert or throw on failure are also supported with non-reverting calls assumed to be successful. Adds safeTransfer, safeTransferFrom, safeApprove, safeDecreaseAllowance, and safeIncreaseAllowance)
Instances include:
DAO\FlashGovernanceArbiter.sol:66: IERC20(flashGovernanceConfig.asset).transferFrom(sender, address(this), flashGovernanceConfig.amount) && //@audit-ok checking return value DAO\FlashGovernanceArbiter.sol:149: IERC20(pendingFlashDecision[targetContract][msg.sender].asset).transfer( DAO\LimboDAO.sol:22: require(IERC20(token).transferFrom(from, to, uint256(amount)), "LimboDAO: ERC20 transfer from failed."); //@audit-ok checking return value DAO\LimboDAO.sol:24: require(IERC20(token).transfer(from, uint256(amount * (-1))), "LimboDAO: ERC20 transfer failed."); //@audit-ok checking return value DAO\LimboDAO.sol:386: require(ERC677(asset).transferFrom(sender, address(this), amount), "LimboDAO: transferFailed"); //@audit-ok checking return value FlanBackstop.sol:87: IERC20(config.flan).transfer(flanLP, normalizedAmount / 4); FlanBackstop.sol:88: IERC20(stablecoin).transferFrom(msg.sender, flanLP, amount / 2); FlanBackstop.sol:95: IERC20(stablecoin).transferFrom(msg.sender, pyroFlanLP, amount / 2); Limbo.sol:216: IERC20(token).transfer(address(crossingConfig.morgothPower), tokenBalance); Limbo.sol:233: IERC20(crossingConfig.behodler).transfer(crossingConfig.ammHelper, adjustedRectangle); UniswapHelper.sol:184: IERC20(VARS.behodler).transfer(pair, rectangleOfFairness); UniswapHelper.sol:192: IERC20(VARS.behodler).transfer(pair, rectangleOfFairness); UniswapHelper.sol:230: IERC20(inputToken).transfer(pair, amount); UniswapHelper.sol:233: ERC20Burnable(VARS.flan).transfer(recipient, reward);
The return values are checked where I marked "@audit-ok" above
VS Code
Consider using safeTransfer/safeTransferFrom or require() consistently.
#0 - gititGoro
2022-02-09T23:06:11Z
duplicate of issue 43
#1 - jack-the-pug
2022-02-27T09:05:02Z
Dup #37
🌟 Selected for report: sirhashalot
298.1789 USDC - $298.18
Dravee
There's a minor precision loss.
Replace:
File: FlanBackstop.sol 88: IERC20(stablecoin).transferFrom(msg.sender, flanLP, amount / 2); ... 95: IERC20(stablecoin).transferFrom(msg.sender, pyroFlanLP, amount / 2); //@audit minor precision loss
with
File: FlanBackstop.sol 88: IERC20(stablecoin).transferFrom(msg.sender, flanLP, amount / 2); ... 95: IERC20(stablecoin).transferFrom(msg.sender, pyroFlanLP, amount - amount / 2);
#0 - gititGoro
2022-02-11T02:22:40Z
I'm aware of the precision loss.
#1 - jack-the-pug
2022-02-27T14:52:56Z
Making this a dup of #232 as the impact is minor.
Dravee
Reading array length at each iteration of the loop takes 6 gas (3 for mload and 3 to place memory_offset) in the stack.
Caching the array length in the stack saves around 3 gas per iteration.
DAO\Proposals\UpdateMultipleSoulConfigProposal.sol:64: for (uint256 i = 0; i < params.length; i++) { DAO\LimboDAO.sol:212: for (uint256 i = 0; i < sushiLPs.length; i++) { DAO\LimboDAO.sol:217: for (uint256 i = 0; i < uniLPs.length; i++) {
VS Code
Store the array's length in a variable before the for-loop, and use it instead.
#0 - gititGoro
2022-02-10T00:09:47Z
duplicate of issue 12
🌟 Selected for report: Dravee
Also found by: CertoraInc, Ruhum, hyh
31.8892 USDC - $31.89
Dravee
SLOADs are expensive (~100 gas) compared to MLOADs/MSTOREs (~3 gas). Minimizing them can save gas.
The code is as such (see @audit tags):
File: FlashGovernanceArbiter.sol 60: function assertGovernanceApproved( 61: address sender, 62: address target, 63: bool emergency 64: ) public { 65: if ( 66: pendingFlashDecision[target][sender].unlockTime < block.timestamp && 67: IERC20(flashGovernanceConfig.asset).transferFrom(sender, address(this), flashGovernanceConfig.amount) //@audit flashGovernanceConfig.amount SLOAD 1 //@audit flashGovernanceConfig.asset SLOAD 1 68: ) { 69: require( 70: emergency || (block.timestamp - security.lastFlashGovernanceAct > security.epochSize), 71: "Limbo: flash governance disabled for rest of epoch" 72: ); 73: pendingFlashDecision[target][sender] = flashGovernanceConfig; 74: pendingFlashDecision[target][sender].unlockTime += block.timestamp; 75: 76: security.lastFlashGovernanceAct = block.timestamp; 77: emit flashDecision(sender, flashGovernanceConfig.asset, flashGovernanceConfig.amount, target); //@audit flashGovernanceConfig.amount SLOAD 2 //@audit flashGovernanceConfig.asset SLOAD 2 78: } else { 79: revert("LIMBO: governance decision rejected."); 80: } 81: }
It's possible to save 2 SLOAD (~200 gas) by caching flashGovernanceConfig.asset
in a address
variable, and flashGovernanceConfig.amount
in a uint256
memory variables and use them instead of reading them repeatedly from storage.
VS Code
Cache the storage values in memory variables and use them instead of repeatedly reading them from storage.
#0 - jack-the-pug
2022-02-16T07:12:47Z
Making this the main issue for all: "Cache the storage values in memory" Gas Optimization
🌟 Selected for report: Ruhum
Also found by: 0v3rf10w, CertoraInc, Dravee, camden, gzeon, hyh, sirhashalot
10.4613 USDC - $10.46
Dravee
The lines are duplicated here: https://github.com/code-423n4/2022-01-behodler/blob/71d8e0cfd9388f975d6a90dffba9b502b222bdfe/contracts/FlanBackstop.sol#L93-L94
redeemRate = PyroTokenLike(config.pyroFlan).redeemRate(); redeemRate = PyroTokenLike(config.pyroFlan).redeemRate();
I don't think the code needs to call this twice
#0 - gititGoro
2022-02-10T00:04:15Z
duplicate of issue 79
#1 - jack-the-pug
2022-02-16T07:29:50Z
Dup #79
17.2202 USDC - $17.22
Dravee
Shortening revert strings to fit in 32 bytes will decrease deployment time gas and will decrease runtime gas when the revert condition is met.
Revert strings that are longer than 32 bytes require at least one additional mstore, along with additional overhead for computing memory offset, etc.
Revert strings > 32 bytes are here:
DAO\FlashGovernanceArbiter.sol:71: "Limbo: flash governance disabled for rest of epoch" DAO\FlashGovernanceArbiter.sol:147: "Limbo: Flashgovernance decision pending." DAO\LimboDAO.sol:270: "LimboDAO: stated proposal does not match current proposal" DAO\LimboDAO.sol:364: "LimboDAO: stake invariant check 2." DAO\LimboDAO.sol:412: "LimboDAO: transfer ownership of limbo and flan." Flan.sol:105: "ERC20: transfer amount exceeds balance"
Visual Studio Code
Shorten the revert strings to fit in 32 bytes.
#0 - gititGoro
2022-02-10T04:24:34Z
duplicate of issue 94
#1 - jack-the-pug
2022-02-20T02:48:18Z
Dup #185
🌟 Selected for report: Dravee
174.9751 USDC - $174.98
Dravee
Looping twice is expensive. It's possible to iterate only once from 0 to maxLength.
Original code iterating between N and 2N times here: https://github.com/code-423n4/2022-01-behodler/blob/71d8e0cfd9388f975d6a90dffba9b502b222bdfe/contracts/DAO/LimboDAO.sol#L195-L222
It's possible to iterate only N times on sushiLPs
and uniLPs
.
Here's the refacto I suggest, it starts from the comment //@audit refacto starts here
and ends with //@audit refacto ends here
:
File: LimboDAO.sol 213: //@audit refacto starts here 214: bool isSushiBigger = sushiLPs.length >= uniLPs.length; 215: (uint256 shortLength, uint256 longLength) = isSushiBigger ? (uniLPs.length, sushiLPs.length) : (sushiLPs.length, uniLPs.length); 216: 217: for (uint256 i; i < shortLength; ++i) { 218: sushiLoop(sushiLPs[i]); 219: uniLoop(uniLPs[i]); 220: } 221: 222: if (isSushiBigger) { 223: for (uint256 i = shortLength; i < longLength; ++i) { 224: sushiLoop(sushiLPs[i]); 225: } 226: } else { 227: for (uint256 i = shortLength; i < longLength; ++i) { 228: uniLoop(uniLPs[i]); 229: } 230: } 231: } 232: 233: function sushiLoop( 234: address sushiLp 235: ) private { 236: require(UniPairLike(sushiLp).factory() == sushiFactory, "LimboDAO: invalid Sushi LP"); 237: if (IERC20(eye).balanceOf(sushiLp) > 1000) assetApproved[sushiLp] = true; 238: fateGrowthStrategy[sushiLp] = FateGrowthStrategy.indirectTwoRootEye; 239: } 240: 241: function uniLoop( 242: address uniLP 243: ) private { 244: require(UniPairLike(uniLP).factory() == uniFactory, "LimboDAO: invalid Uni LP"); //@audit-info Dravee: I corrected the comment 245: if (IERC20(eye).balanceOf(uniLP) > 1000) assetApproved[uniLP] = true; 246: fateGrowthStrategy[uniLP] = FateGrowthStrategy.indirectTwoRootEye; 247: } 248: //@audit refacto ends here
VS Code
Apply the suggested refacto
13.2841 USDC - $13.28
Dravee
If a variable is not set/initialized, it is assumed to have the default value (0, false, 0x0 etc depending on the data type). Explicitly initializing it with its default value is an anti-pattern and wastes gas.
Instances include:
DAO\Proposals\UpdateMultipleSoulConfigProposal.sol:64: for (uint256 i = 0; i < params.length; i++) { DAO\LimboDAO.sol:212: for (uint256 i = 0; i < sushiLPs.length; i++) { DAO\LimboDAO.sol:217: for (uint256 i = 0; i < uniLPs.length; i++) { Flan.sol:15: uint8 public burnOnTransferFee = 0; //% between 1 and 100, recipient pays Limbo.sol:558: uint256 flanBonus = 0;
Manual Analysis
Remove explicit initialization for default values.
#0 - gititGoro
2022-02-10T04:17:17Z
Duplicate of issue 20
#1 - jack-the-pug
2022-02-27T12:43:40Z
Dup #20
22.9602 USDC - $22.96
Dravee
Strict inequalities add a check of non equality which costs around 3 gas.
The following should use an inclusive inequality to save gas:
File: LimboDAO.sol 287: uint256 cost = fate > 0 ? uint256(fate) : uint256(-fate);
VS Code
Use >=
instead of >
#0 - gititGoro
2022-02-11T02:50:32Z
duplicate of issue 15
#1 - jack-the-pug
2022-02-22T14:42:22Z
Dup #15
Dravee
++i
costs less gas compared to i++
for unsigned integer, as pre-increment is cheaper (about 5 gas per iteration)
i++
increments i
and returns the initial value of i
. Which means:
uint i = 1; i++; // == 1 but i == 2
But ++i
returns the actual incremented value:
uint i = 1; ++i; // == 2 and i == 2 too, so no need for a temporary variable
In the first case, the compiler has to create a temporary variable (when used) for returning 1
instead of 2
Instances include:
DAO\Proposals\UpdateMultipleSoulConfigProposal.sol:64: for (uint256 i = 0; i < params.length; i++) { DAO\LimboDAO.sol:212: for (uint256 i = 0; i < sushiLPs.length; i++) { DAO\LimboDAO.sol:217: for (uint256 i = 0; i < uniLPs.length; i++) {
VS Code
Use ++i
instead of i++
to increment the value of an uint variable.
#0 - jack-the-pug
2022-02-22T14:54:43Z
Dup #10
🌟 Selected for report: Dravee
Also found by: CertoraInc, defsec, pauliax, sirhashalot
22.9602 USDC - $22.96
Dravee
Increased gas cost due to unnecessary automatic underflow checks.
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, or the operation doesn't depend on user input), some gas can be saved by using an unchecked
block.
https://docs.soliditylang.org/en/v0.8.10/control-structures.html#checked-or-unchecked-arithmetic
See the @audit tags, lines 180 and 183 can't underflow due to the conditional flow:
File: FlashGovernanceArbiter.sol 178: if (v1 > v2) { 179: if (v2 == 0) require(v1 <= 1, "FE1"); 180: else require(((v1 - v2) * 100) < security.changeTolerance * v1, "FE1"); //@audit should be unchecked 181: } else { 182: if (v1 == 0) require(v2 <= 1, "FE1"); 183: else require(((v2 - v1) * 100) < security.changeTolerance * v1, "FE1");//@audit should be unchecked 184: }
VS Code
Lines 180's (v1 - v2)
and 183's (v2 - v1)
should be inside unchecked
blocks.
#0 - gititGoro
2022-02-10T11:54:39Z
duplicate of issue 116
17.2202 USDC - $17.22
Dravee
For maps that use the same key value: having separate fields is error prone (like in case of deletion or future new fields).
In struct ConfigVars
, the 4 maps use the same address
:
File: FlanBackstop.sol 39: struct ConfigVars { 40: address flan; 41: address pyroFlan; 42: mapping(address => address) flanLPs; 43: mapping(address => address) pyroFlanLPs; 44: mapping(address => uint256) acceptableHighestPrice; 45: mapping(address => uint8) decimalPlaces; 46: }
See their usage below, it's the exact same address stablecoin
:
File: FlanBackstop.sol 57: function setBacker( 58: address stablecoin, 59: address flanLP, 60: address pyroFlanLP, 61: uint256 acceptableHighestPrice, 62: uint8 decimalPlaces 63: ) external onlySuccessfulProposal { 64: config.flanLPs[stablecoin] = flanLP; 65: config.pyroFlanLPs[stablecoin] = pyroFlanLP; 66: config.acceptableHighestPrice[stablecoin] = acceptableHighestPrice; 67: config.decimalPlaces[stablecoin] = decimalPlaces; 68: } ... File: FlanBackstop.sol 77: address flanLP = config.flanLPs[stablecoin]; 78: address pyroFlanLP = config.pyroFlanLPs[stablecoin];
VS Code
I'd suggest these 4 related data get grouped in a struct, let's name it FlanLpsInfo
:
struct FlanLpsInfo{ address flanLPs; address pyroFlanLPs; uint256 acceptableHighestPrice; uint8 decimalPlaces; }
And it would be used as a state variable in this manner:
mapping(address => FlanLpsInfo) flanLpsInfo;
Then, you can delete all related fields with a simple delete flanLpsInfo[stablecoin]
.
#0 - gititGoro
2022-02-10T04:44:52Z
duplicate of issue number 3
#1 - jack-the-pug
2022-02-27T11:56:22Z
Dup #222
🌟 Selected for report: BouSalman
Also found by: CertoraInc, Dravee
47.2433 USDC - $47.24
Dravee
A division by 2 can be calculated by shifting one to the right.
While the DIV
opcode uses 5 gas, the SHR
opcode only uses 3 gas. Furthermore, Solidity's division operation also includes a division-by-0 prevention which is bypassed using shifting.
Instances include:
FlanBackstop.sol:86: FlanLike(config.flan).mint(address(this), normalizedAmount / 2); FlanBackstop.sol:88: IERC20(stablecoin).transferFrom(msg.sender, flanLP, amount / 2); FlanBackstop.sol:95: IERC20(stablecoin).transferFrom(msg.sender, pyroFlanLP, amount / 2); FlanBackstop.sol:108: uint256 premium = (flanToMint * (growth / 2)) / 100; Limbo.sol:225: adjustedRectangle = scxMinted / 2;
VS Code
Replace / 2
with >> 1
#0 - gititGoro
2022-02-09T23:22:02Z
duplicate of issue 95
#1 - jack-the-pug
2022-02-27T12:14:23Z
Dup #95
🌟 Selected for report: Dravee
174.9751 USDC - $174.98
Dravee
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.
Consequences: each usage of a "constant" costs ~100gas more on each access (it is still a little better than storing the result in storage, but not much..). since these are not real constants, they can't be referenced from a real constant environment (e.g. from assembly, or from another library )
UniswapHelper.sol:56: uint256 constant year = (1 days * 365);
VS Code
Replace with:
UniswapHelper.sol:56: uint256 constant year = 365 days;
#0 - gititGoro
2022-02-05T00:56:13Z
That's surprising and disappointing.
#1 - gititGoro
2022-07-04T14:42:33Z
🌟 Selected for report: Dravee
Also found by: Tomio, csanuragjain
47.2433 USDC - $47.24
Dravee
Checking non-zero transfer values can avoid an external call to save gas.
Here, the code is as such:
File: LimboDAO.sol 21: if (amount > 0) { 22: require(IERC20(token).transferFrom(from, to, uint256(amount)), "LimboDAO: ERC20 transfer from failed."); 23: } else { 24: require(IERC20(token).transfer(from, uint256(amount * (-1))), "LimboDAO: ERC20 transfer failed."); 25: }
while it should be as such to avoid making an unnecessary external call:
File: LimboDAO.sol 21: if (amount > 0) { 22: require(IERC20(token).transferFrom(from, to, uint256(amount)), "LimboDAO: ERC20 transfer from failed."); 23: } else if (amount != 0) { 24: require(IERC20(token).transfer(from, uint256(amount * (-1))), "LimboDAO: ERC20 transfer failed."); 25: }
VS Code
Check if amount != 0.
🌟 Selected for report: Dravee
174.9751 USDC - $174.98
Dravee
Some of the require statements can be placed earlier to reduce gas usage on revert.
From:
File: UniswapHelper.sol 114: function configure( 115: address _limbo, 116: address FlanSCXPair, 117: address behodler, 118: address flan, 119: uint256 divergenceTolerance, 120: uint256 minQuoteWaitDuration, 121: uint8 precision, 122: uint8 priceBoostOvershoot 123: ) public onlySuccessfulProposal { 124: limbo = _limbo; 125: VARS.Flan_SCX_tokenPair = UniPairLike(FlanSCXPair); 126: VARS.behodler = behodler; 127: VARS.flan = flan; 128: require(divergenceTolerance >= 100, "Divergence of 100 is parity"); //@audit should move up before lots of SSTOREs 129: VARS.divergenceTolerance = divergenceTolerance; 130: VARS.minQuoteWaitDuration = minQuoteWaitDuration; 131: VARS.DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F; 132: VARS.precision = precision == 0 ? precision : precision; 133: require(priceBoostOvershoot < 100, "Set overshoot to number between 1 and 100.");//@audit should move up before lots of SSTOREs 134: VARS.priceBoostOvershoot = priceBoostOvershoot; 135: }
to
File: UniswapHelper.sol 114: function configure( 115: address _limbo, 116: address FlanSCXPair, 117: address behodler, 118: address flan, 119: uint256 divergenceTolerance, 120: uint256 minQuoteWaitDuration, 121: uint8 precision, 122: uint8 priceBoostOvershoot 123: ) public onlySuccessfulProposal { 124: require(divergenceTolerance >= 100, "Divergence of 100 is parity"); //@audit-info moved 125: require(priceBoostOvershoot < 100, "Set overshoot to number between 1 and 100.");//@audit-info moved 126: 127: limbo = _limbo; 128: VARS.Flan_SCX_tokenPair = UniPairLike(FlanSCXPair); 129: VARS.behodler = behodler; 130: VARS.flan = flan; 131: VARS.divergenceTolerance = divergenceTolerance; 132: VARS.minQuoteWaitDuration = minQuoteWaitDuration; 133: VARS.DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F; 134: VARS.precision = precision == 0 ? precision : precision; 135: VARS.priceBoostOvershoot = priceBoostOvershoot; 136: }
Up to 8 SSTOREs can be saved on revert this way (8 * 20 000 = 160 000 gas)
VS Code
Reorder these require statements
#0 - gititGoro
2022-02-05T02:25:26Z
Configuration and Seed functions don't need to be optimized.