You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Arithmetics: ++i costs less gas compared to i++ or i += 1
++i costs less gas compared to i++ or i += 1 for unsigned integers, as pre-increment is cheaper (about 5 gas per iteration). This statement is true even with the optimizer enabled.
i++ increments i and returns the initial value of i. Which means:
uinti=1;i++;// == 1 but i == 2
But ++i returns the actual incremented value:
uinti=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, thus it costs more gas.
The same logic applies for --i and i--.
Consider using ++i instead of i++ or i += 1 in the following instances:
Arithmetics: Use != 0 instead of > 0 for unsigned integers
uint will never go below 0. Thus, > 0 is gas inefficient in comparisons as checking if != 0 is sufficient and costs less gas.
Consider changing > 0 to != 0 in these lines:
contracts/core/promise/PromiseRouter.sol:
259: if(callbackFee>0){contracts/core/promise/libraries/PromiseMessage.sol:
142: return_length>0&&(RETURNDATA_START+_length)==_len;contracts/core/connext/helpers/ConnextPriceOracle.sol:
150: require(baseTokenPrice>0,"invalid base token");contracts/core/connext/helpers/SponsorVault.sol:
217: if(sponsoredFee>0){contracts/core/connext/helpers/StableSwap.sol:
82: if(i>0){contracts/core/connext/libraries/LibDiamond.sol:
121: require(_functionSelectors.length>0,"LibDiamondCut: No selectors in facet to cut");139: require(_functionSelectors.length>0,"LibDiamondCut: No selectors in facet to cut");158: require(_functionSelectors.length>0,"LibDiamondCut: No selectors in facet to cut");226: require(_calldata.length>0,"LibDiamondCut: _calldata is empty but _init is not address(0)");232: if(error.length>0){247: require(contractSize>0,_errorMessage);contracts/core/connext/libraries/AmplificationUtils.sol:
86: require(futureA_>0&&futureA_<MAX_A,"futureA_ must be > 0 and < MAX_A");contracts/core/connext/libraries/SwapUtils.sol:
369: if(supply>0){670: if(dyAdminFee>0){711: if(dxAdminFee>0){765: if(dyAdminFee>0){799: if(dxAdminFee>0){845: require(v.totalSupply!=0||amounts[i]>0,"Must supply all tokens in pool");965: if(adminFee>0){contracts/core/connext/facets/BridgeFacet.sol:
293: if(_args.params.callback==address(0)&&_args.params.callbackFee>0){499: if(_amount>0){665: if(pathLength>0)// make sure routers are all approved if neededcontracts/core/connext/facets/StableSwapFacet.sol:
416: if(i>0){
Visibility: Consider declaring constants as non-public to save gas
If a constant is not used outside of its contract, declaring it as private or internal instead of public can save gas.
Consider changing the visibility of the following from public to internal or private:
Visibility: public functions can be set to external
Calls to external functions are cheaper than public functions. Thus, if a function is not used internally in any contract, it should be set to external to save gas and improve code readability.
Consider changing following functions from public to external:
Errors: Reduce the length of error messages (long revert strings)
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.
In these instances, consider shortening the revert strings to fit within 32 bytes, or using custom errors:
contracts/core/connext/libraries/LibDiamond.sol:
66: require(msg.sender==diamondStorage().contractOwner,"LibDiamond: Must be contract owner");121: require(_functionSelectors.length>0,"LibDiamondCut: No selectors in facet to cut");123: require(_facetAddress!=address(0),"LibDiamondCut: Add facet can't be address(0)");132: require(oldFacetAddress==address(0),"LibDiamondCut: Can't add function that already exists");139: require(_functionSelectors.length>0,"LibDiamondCut: No selectors in facet to cut");141: require(_facetAddress!=address(0),"LibDiamondCut: Add facet can't be address(0)");150: require(oldFacetAddress!=_facetAddress,"LibDiamondCut: Can't replace function with same function");158: require(_functionSelectors.length>0,"LibDiamondCut: No selectors in facet to cut");161: require(_facetAddress==address(0),"LibDiamondCut: Remove facet address must be address(0)");191: require(_facetAddress!=address(0),"LibDiamondCut: Can't remove function that doesn't exist");193: require(_facetAddress!=address(this),"LibDiamondCut: Can't remove immutable function");224: require(_calldata.length==0,"LibDiamondCut: _init is address(0) but_calldata is not empty");226: require(_calldata.length>0,"LibDiamondCut: _calldata is empty but _init is not address(0)");contracts/core/connext/libraries/SwapUtils.sol:
697: require(dy<=self.balances[tokenIndexTo],"Cannot get more than pool balance");784: require(dy<=self.balances[tokenIndexTo],"Cannot get more than pool balance");
Errors: Use multiple require statements instead of &&
Instead of using a single require statement with the && operator, using multiple require statements would help to save runtime gas cost. However, note that this results in a higher deployment gas cost, which is a fair trade-off.
A require statement can be split as such:
// Original code:require(a&&b,'error');// Changed to:require(a,'error: a');require(b,'error: b');
Instances where multiple require statements should be used:
contracts/core/connext/libraries/AmplificationUtils.sol:
86: require(futureA_>0&&futureA_<MAX_A,"futureA_ must be > 0 and < MAX_A");contracts/core/connext/libraries/SwapUtils.sol:
397: require(tokenIndexFrom<numTokens&&tokenIndexTo<numTokens,"Tokens must be in pool");493: require(tokenIndexFrom<xp.length&&tokenIndexTo<xp.length,"Token index out of range");524: require(tokenIndexFrom<xp.length&&tokenIndexTo<xp.length,"Token index out of range");1007: require(maxBurnAmount<=v.lpToken.balanceOf(msg.sender)&&maxBurnAmount!=0,">LP.balanceOf");
Errors: Use custom errors instead of revert strings
Since Solidity v0.8.4, custom errors should be used instead of revert strings due to:
Starting from Solidity v0.8.4, there is a convenient and gas-efficient way to explain to users why an operation failed through the use of custom errors. Until now, you could already use strings to give more information about failures (e.g., revert("Insufficient funds.");), but they are rather expensive, especially when it comes to deploy cost, and it is difficult to use dynamic information in them.
Custom errors can be defined using of the error statement, both inside or outside of contracts.
Instances where custom errors can be used instead:
contracts/core/connext/helpers/ConnextPriceOracle.sol:
72: require(msg.sender==admin,"caller is not the admin");150: require(baseTokenPrice>0,"invalid base token");contracts/core/connext/helpers/Executor.sol:
57: require(msg.sender==connext,"#OC:027");contracts/core/connext/helpers/StableSwap.sol:
75: require(_pooledTokens.length>1,"_pooledTokens.length <= 1");76: require(_pooledTokens.length<=32,"_pooledTokens.length > 32");77: require(_pooledTokens.length==decimals.length,"_pooledTokens decimals mismatch");89: require(address(_pooledTokens[i])!=address(0),"The 0 address isn't an ERC-20");90: require(decimals[i]<=SwapUtils.POOL_PRECISION_DECIMALS,"Token decimals exceeds max");96: require(_a<AmplificationUtils.MAX_A,"_a exceeds maximum");97: require(_fee<SwapUtils.MAX_SWAP_FEE,"_fee exceeds maximum");98: require(_adminFee<SwapUtils.MAX_ADMIN_FEE,"_adminFee exceeds maximum");102: require(lpToken.initialize(lpTokenName,lpTokenSymbol),"could not init lpToken clone");125: require(block.timestamp<=deadline,"Deadline not met");155: require(index<swapStorage.pooledTokens.length,"Out of range");167: require(address(getToken(index))==tokenAddress,"Token does not exist");177: require(index<swapStorage.pooledTokens.length,"Index out of range");contracts/core/connext/helpers/LPToken.sol:
35: require(amount!=0,"LPToken: cannot mint 0");50: require(to!=address(this),"LPToken: cannot send to itself");contracts/core/connext/libraries/ConnextMessage.sol:
116: require(isValidAction(_action),"!action");contracts/core/connext/libraries/LibDiamond.sol:
66: require(msg.sender==diamondStorage().contractOwner,"LibDiamond: Must be contract owner");121: require(_functionSelectors.length>0,"LibDiamondCut: No selectors in facet to cut");123: require(_facetAddress!=address(0),"LibDiamondCut: Add facet can't be address(0)");132: require(oldFacetAddress==address(0),"LibDiamondCut: Can't add function that already exists");139: require(_functionSelectors.length>0,"LibDiamondCut: No selectors in facet to cut");141: require(_facetAddress!=address(0),"LibDiamondCut: Add facet can't be address(0)");150: require(oldFacetAddress!=_facetAddress,"LibDiamondCut: Can't replace function with same function");158: require(_functionSelectors.length>0,"LibDiamondCut: No selectors in facet to cut");161: require(_facetAddress==address(0),"LibDiamondCut: Remove facet address must be address(0)");191: require(_facetAddress!=address(0),"LibDiamondCut: Can't remove function that doesn't exist");193: require(_facetAddress!=address(this),"LibDiamondCut: Can't remove immutable function");224: require(_calldata.length==0,"LibDiamondCut: _init is address(0) but_calldata is not empty");226: require(_calldata.length>0,"LibDiamondCut: _calldata is empty but _init is not address(0)");contracts/core/connext/libraries/AmplificationUtils.sol:
84: require(block.timestamp>=self.initialATime.add(1days),"Wait 1 day before starting ramp");85: require(futureTime_>=block.timestamp.add(MIN_RAMP_TIME),"Insufficient ramp time");86: require(futureA_>0&&futureA_<MAX_A,"futureA_ must be > 0 and < MAX_A");92: require(futureAPrecise.mul(MAX_A_CHANGE)>=initialAPrecise,"futureA_ is too small");94: require(futureAPrecise<=initialAPrecise.mul(MAX_A_CHANGE),"futureA_ is too large");111: require(self.futureATime>block.timestamp,"Ramp is already stopped");contracts/core/connext/libraries/SwapUtils.sol:
191: require(tokenIndex<xp.length,"Token index out of range");198: require(tokenAmount<=xp[tokenIndex],"Withdraw exceeds available");248: require(tokenIndex<numTokens,"Token not found");342: require(numTokens==precisionMultipliers.length,"Balances must match multipliers");396: require(tokenIndexFrom!=tokenIndexTo,"Can't compare token to itself");397: require(tokenIndexFrom<numTokens&&tokenIndexTo<numTokens,"Tokens must be in pool");493: require(tokenIndexFrom<xp.length&&tokenIndexTo<xp.length,"Token index out of range");524: require(tokenIndexFrom<xp.length&&tokenIndexTo<xp.length,"Token index out of range");554: require(amount<=totalSupply,"Cannot exceed total supply");615: require(index<self.pooledTokens.length,"Token index out of range");649: require(dx<=tokenFrom.balanceOf(msg.sender),"Cannot swap more than you own");662: require(dy>=minDy,"Swap didn't result in min tokens");697: require(dy<=self.balances[tokenIndexTo],"Cannot get more than pool balance");703: require(dx<=maxDx,"Swap needs more than max tokens");717: require(dx<=tokenFrom.balanceOf(msg.sender),"Cannot swap more than you own");723: require(dx==tokenFrom.balanceOf(address(this)).sub(beforeBalance),"not support fee token");750: require(dx<=tokenFrom.balanceOf(msg.sender),"Cannot swap more than you own");756: require(dy>=minDy,"Swap didn't result in min tokens");784: require(dy<=self.balances[tokenIndexTo],"Cannot get more than pool balance");790: require(dx<=maxDx,"Swap didn't result in min tokens");823: require(amounts.length==pooledTokens.length,"Amounts must match pooled tokens");845: require(v.totalSupply!=0||amounts[i]>0,"Must supply all tokens in pool");861: require(v.d1>v.d0,"D should increase");890: require(toMint>=minToMint,"Couldn't mint min requested");916: require(amount<=lpToken.balanceOf(msg.sender),">LP.balanceOf");917: require(minAmounts.length==pooledTokens.length,"minAmounts must match poolTokens");925: require(amounts[i]>=minAmounts[i],"amounts[i] < minAmounts[i]");954: require(tokenAmount<=lpToken.balanceOf(msg.sender),">LP.balanceOf");955: require(tokenIndex<pooledTokens.length,"Token not found");961: require(dy>=minAmount,"dy < minAmount");1005: require(amounts.length==pooledTokens.length,"Amounts should match pool tokens");1007: require(maxBurnAmount<=v.lpToken.balanceOf(msg.sender)&&maxBurnAmount!=0,">LP.balanceOf");1032: require(tokenAmount!=0,"Burnt amount cannot be zero");1035: require(tokenAmount<=maxBurnAmount,"tokenAmount > maxBurnAmount");1071: require(newAdminFee<=MAX_ADMIN_FEE,"Fee is too high");1084: require(newSwapFee<=MAX_SWAP_FEE,"Fee is too high");contracts/core/connext/facets/BaseConnextFacet.sol:
38: require(s._status!=_ENTERED,"ReentrancyGuard: reentrant call");125: require(_remote!=bytes32(0),"!remote");
Unnecessary initialization of variables with default values
Uninitialized variables are assigned with a default value depending on its type:
uint: 0
bool: false
address: address(0)
Thus, explicitly initializing a variable with its default value costs unnecesary gas. For example, the following code:
boolb=false;addressc=address(0);uint256a=0;
can be changed to:
uint256a;boolb;addressc;
Consider declaring the following lines without explicitly setting a value:
Some variables are defined even though they are only used once in their respective functions. Not defining these variables can help to reduce gas cost and contract size.
Instances include:
contracts/core/connext/libraries/LibDiamond.sol:
131: addressoldFacetAddress=ds.selectorToFacetAndPosition[selector].facetAddress;164: addressoldFacetAddress=ds.selectorToFacetAndPosition[selector].facetAddress;contracts/core/connext/libraries/AssetLogic.sol:
388: IStableSwappool=s.adoptedToLocalPools[id];422: IStableSwappool=s.adoptedToLocalPools[id];contracts/core/connext/libraries/SwapUtils.sol:
367: LPTokenlpToken=self.lpToken;749: IERC20tokenFrom=self.pooledTokens[tokenIndexFrom];1056: IERC20token=pooledTokens[i];contracts/core/connext/facets/PortalFacet.sol:
88: uint256routerBalance=s.routerBalances[msg.sender][_local];// in local
Storage variables should be declared immutable when possible
If a storage variable is assigned only in the constructor, it should be declared as immutable. This would help to reduce gas costs as calls to immutable variables are much cheaper than regular state variables, as seen from the Solidity Docs:
Compared to regular state variables, the gas costs of constant and immutable variables are much lower. Immutable variables are evaluated once at construction time and their value is copied to all the places in the code where they are accessed.
Variables declared as constant are expressions, not constants
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.
If the variable was immutable instead: the calculation would only be done once at deploy time (in the constructor), and then the result would be saved and read directly at runtime rather than being recalculated.
Consequences: each usage of a “constant” costs ~100 gas 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)
Change these expressions from constant to immutable and implement the calculation in the constructor. Alternatively, hardcode these values in the constants and add a comment to say how the value was calculated.
The text was updated successfully, but these errors were encountered:
Gas Report
For-loops: Index initialized with default value
Uninitialized
uint
variables are assigned with a default value of0
.Thus, in for-loops, explicitly initializing an index with
0
costs unnecesary gas. For example, the following code:can be changed to:
Consider declaring the following lines without explicitly setting the index to
0
:For-Loops: Cache array length outside of loops
Reading an array length at each iteration of the loop takes 6 gas (3 for
mload
and 3 to placememory_offset
) in the stack.Caching the array length in the stack saves around 3 gas per iteration.
For example:
can be changed to:
Consider making the following change to these lines:
For-Loops: Index increments can be left unchecked
From Solidity v0.8 onwards, all arithmetic operations come with implicit overflow and underflow checks.
In for-loops, as it is impossible for the index to overflow, it can be left unchecked to save gas every iteration.
For example, the code below:
can be changed to:
Consider making the following change to these lines:
Arithmetics:
++i
costs less gas compared toi++
ori += 1
++i
costs less gas compared toi++
ori += 1
for unsigned integers, as pre-increment is cheaper (about 5 gas per iteration). This statement is true even with the optimizer enabled.i++
incrementsi
and returns the initial value ofi
. Which means:But
++i
returns the actual incremented value:In the first case, the compiler has to create a temporary variable (when used) for returning
1
instead of2
, thus it costs more gas.The same logic applies for
--i
andi--
.Consider using
++i
instead ofi++
ori += 1
in the following instances:Arithmetics: Use
!= 0
instead of> 0
for unsigned integersuint
will never go below 0. Thus,> 0
is gas inefficient in comparisons as checking if!= 0
is sufficient and costs less gas.Consider changing
> 0
to!= 0
in these lines:Visibility: Consider declaring constants as non-public to save gas
If a constant is not used outside of its contract, declaring it as
private
orinternal
instead ofpublic
can save gas.Consider changing the visibility of the following from
public
tointernal
orprivate
:Visibility:
public
functions can be set toexternal
Calls to
external
functions are cheaper thanpublic
functions. Thus, if a function is not used internally in any contract, it should be set toexternal
to save gas and improve code readability.Consider changing following functions from
public
toexternal
:Errors: Reduce the length of error messages (long revert strings)
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.In these instances, consider shortening the revert strings to fit within 32 bytes, or using custom errors:
Errors: Use multiple
require
statements instead of&&
Instead of using a single
require
statement with the&&
operator, using multiplerequire
statements would help to save runtime gas cost. However, note that this results in a higher deployment gas cost, which is a fair trade-off.A
require
statement can be split as such:Instances where multiple
require
statements should be used:Errors: Use custom errors instead of revert strings
Since Solidity v0.8.4, custom errors should be used instead of revert strings due to:
Taken from Custom Errors in Solidity:
Custom errors can be defined using of the
error
statement, both inside or outside of contracts.Instances where custom errors can be used instead:
Unnecessary initialization of variables with default values
Uninitialized variables are assigned with a default value depending on its type:
uint
:0
bool
:false
address
:address(0)
Thus, explicitly initializing a variable with its default value costs unnecesary gas. For example, the following code:
can be changed to:
Consider declaring the following lines without explicitly setting a value:
Unnecessary definition of variables
Some variables are defined even though they are only used once in their respective functions. Not defining these variables can help to reduce gas cost and contract size.
Instances include:
Storage variables should be declared
immutable
when possibleIf a storage variable is assigned only in the constructor, it should be declared as
immutable
. This would help to reduce gas costs as calls toimmutable
variables are much cheaper than regular state variables, as seen from the Solidity Docs:Consider declaring these variables as
immutable
:Variables declared as
constant
are expressions, not constantsDue to how
constant
variables are implemented (replacements at compile-time), an expression assigned to aconstant
variable is recomputed each time that the variable is used, which wastes some gas.If the variable was
immutable
instead: the calculation would only be done once at deploy time (in the constructor), and then the result would be saved and read directly at runtime rather than being recalculated.See: ethereum/solidity#9232:
Change these expressions from
constant
toimmutable
and implement the calculation in the constructor. Alternatively, hardcode these values in the constants and add a comment to say how the value was calculated.The text was updated successfully, but these errors were encountered: