Platform: Code4rena
Start Date: 08/06/2022
Pot Size: $115,000 USDC
Total HM: 26
Participants: 72
Period: 11 days
Judge: leastwood
Total Solo HM: 14
Id: 132
League: ETH
Rank: 42/72
Findings: 2
Award: $227.15
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: BowTiedWardens
Also found by: 0x1f8b, 0x29A, 0x52, 0xNazgul, 0xNineDec, 0xf15ers, 0xkatana, 0xmint, Chom, ElKu, Funen, IllIllI, JMukesh, Jujic, Kaiziron, Lambda, MiloTruck, Ruhum, SmartSek, SooYa, TerrierLover, TomJ, WatchPug, Waze, _Adam, asutorufos, auditor0517, bardamu, c3phas, catchup, cccz, ch13fd357r0y3r, cloudjunky, cmichel, cryptphi, csanuragjain, defsec, fatherOfBlocks, hansfriese, hyh, jayjonah8, joestakey, k, kenta, obtarian, oyc_109, robee, sach1r0, shenwilly, simon135, slywaters, sorrynotsorry, tintin, unforgiven, xiaoming90, zzzitron
141.8225 USDC - $141.82
Information : L005 - Do not use Deprecated Library Functions SafeERC20.sol - safeApprove
core/connext/libraries/AssetLogic.sol:347: SafeERC20.safeApprove(IERC20(_assetIn), address(pool), _amountIn);
Usage of deprecated library functions, for instance safeApprove
from OpenZeppelin's SafeERC20
library is discouraged, it is recommended to use safeIncreaseAllowance
and safeDecreaseAllowance
instead, for example :
core/connext/libraries/AssetLogic.sol:347: SafeERC20.safeIncreaseAllowance(IERC20(_assetIn), address(pool), _amountIn);
#0 - liu-zhipeng
2022-06-30T13:18:54Z
fixed
#1 - jakekidd
2022-07-02T01:30:17Z
approval needs to be reset to 0 and then increased, so we are stuck using safeApprove
method in order to do so
I suppose this issue is still valid, however
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0x29A, 0xKitsune, 0xNazgul, 0xf15ers, 0xkatana, 0xmint, BowTiedWardens, ElKu, Fitraldys, Funen, Kaiziron, Lambda, Metatron, MiloTruck, Randyyy, Ruhum, SmartSek, TomJ, Tomio, UnusualTurtle, Waze, _Adam, apostle0x01, asutorufos, c3phas, catchup, csanuragjain, defsec, fatherOfBlocks, hansfriese, hyh, ignacio, joestakey, k, kaden, nahnah, oyc_109, rfa, robee, sach1r0, simon135, slywaters
85.3344 USDC - $85.33
!= 0 will do the same as > 0 for unsigned integers, but != 0 costs less gas compared to > 0 for unsigned integers in require statements with the optimizer enabled.
core/connext/libraries/SwapUtils.sol:845: require(v.totalSupply != 0 || amounts[i] > 0, "Must supply all tokens in pool"); core/connext/libraries/AmplificationUtils.sol:86: require(futureA_ > 0 && futureA_ < MAX_A, "futureA_ must be > 0 and < MAX_A"); core/connext/libraries/LibDiamond.sol:121: require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut"); core/connext/libraries/LibDiamond.sol:139: require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut"); core/connext/libraries/LibDiamond.sol:158: require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut"); core/connext/libraries/LibDiamond.sol:226: require(_calldata.length > 0, "LibDiamondCut: _calldata is empty but _init is not address(0)"); core/connext/libraries/LibDiamond.sol:247: require(contractSize > 0, _errorMessage); core/connext/helpers/ConnextPriceOracle.sol:150: require(baseTokenPrice > 0, "invalid base token");
It is recommended to replace > 0
with != 0
, as they do the same thing for unsigned integers, and '!= 0' costs less gas compared to > 0
in require statements with the optimizer enabled, also enable the optimizer.
For example :
core/connext/helpers/ConnextPriceOracle.sol:150: require(baseTokenPrice != 0, "invalid base token");
Uninitialized variables are assigned with the default value of their type, initializing a variable with its default value costs unnecessary gas.
core/relayer-fee/libraries/RelayerFeeMessage.sol:81: for (uint256 i = 0; i < length; ) { core/shared/Version.sol:9: uint8 public constant VERSION = 0; core/connext/facets/VersionFacet.sol:16: uint8 internal immutable _version = 0; core/connext/facets/BridgeFacet.sol:68: uint16 public constant AAVE_REFERRAL_CODE = 0; core/connext/facets/StableSwapFacet.sol:415: for (uint8 i = 0; i < _pooledTokens.length; i++) { core/connext/libraries/Encoding.sol:22: for (uint8 i = 0; i < 10; i += 1) { core/connext/libraries/SwapUtils.sol:205: for (uint256 i = 0; i < xp.length; i++) { core/connext/libraries/SwapUtils.sol:254: for (uint256 i = 0; i < numTokens; i++) { core/connext/libraries/SwapUtils.sol:268: for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { core/connext/libraries/SwapUtils.sol:289: for (uint256 i = 0; i < numTokens; i++) { core/connext/libraries/SwapUtils.sol:300: for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { core/connext/libraries/SwapUtils.sol:302: for (uint256 j = 0; j < numTokens; j++) { core/connext/libraries/SwapUtils.sol:344: for (uint256 i = 0; i < numTokens; i++) { core/connext/libraries/SwapUtils.sol:405: for (uint256 i = 0; i < numTokens; i++) { core/connext/libraries/SwapUtils.sol:425: for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { core/connext/libraries/SwapUtils.sol:558: for (uint256 i = 0; i < balances.length; i++) { core/connext/libraries/SwapUtils.sol:591: for (uint256 i = 0; i < balances.length; i++) { core/connext/libraries/SwapUtils.sol:844: for (uint256 i = 0; i < pooledTokens.length; i++) { core/connext/libraries/SwapUtils.sol:869: for (uint256 i = 0; i < pooledTokens.length; i++) { core/connext/libraries/SwapUtils.sol:924: for (uint256 i = 0; i < amounts.length; i++) { core/connext/libraries/SwapUtils.sol:1014: for (uint256 i = 0; i < pooledTokens.length; i++) { core/connext/libraries/SwapUtils.sol:1019: for (uint256 i = 0; i < pooledTokens.length; i++) { core/connext/libraries/SwapUtils.sol:1039: for (uint256 i = 0; i < pooledTokens.length; i++) { core/connext/libraries/SwapUtils.sol:1055: for (uint256 i = 0; i < pooledTokens.length; i++) { core/connext/helpers/Multicall.sol:16: for (uint256 i = 0; i < calls.length; i++) { core/connext/helpers/StableSwap.sol:81: for (uint8 i = 0; i < _pooledTokens.length; i++) { core/connext/helpers/ConnextPriceOracle.sol:176: for (uint256 i = 0; i < tokenAddresses.length; i++) {
It is recommended to initialize variables without assigning them the default value, for example :
core/connext/helpers/ConnextPriceOracle.sol:176: for (uint256 i; i < tokenAddresses.length; i++) {
Caching the array length outside a loop saves reading it on each iteration, as long as the array's length is not changed during the loop.
core/connext/facets/RelayerFacet.sol:140: for (uint256 i; i < _transferIds.length; ) { core/connext/facets/RelayerFacet.sol:164: for (uint256 i; i < _transferIds.length; ) { core/connext/facets/StableSwapFacet.sol:415: for (uint8 i = 0; i < _pooledTokens.length; i++) { core/connext/libraries/SwapUtils.sol:205: for (uint256 i = 0; i < xp.length; i++) { core/connext/libraries/SwapUtils.sol:558: for (uint256 i = 0; i < balances.length; i++) { core/connext/libraries/SwapUtils.sol:591: for (uint256 i = 0; i < balances.length; i++) { core/connext/libraries/SwapUtils.sol:844: for (uint256 i = 0; i < pooledTokens.length; i++) { core/connext/libraries/SwapUtils.sol:869: for (uint256 i = 0; i < pooledTokens.length; i++) { core/connext/libraries/SwapUtils.sol:924: for (uint256 i = 0; i < amounts.length; i++) { core/connext/libraries/SwapUtils.sol:1014: for (uint256 i = 0; i < pooledTokens.length; i++) { core/connext/libraries/SwapUtils.sol:1019: for (uint256 i = 0; i < pooledTokens.length; i++) { core/connext/libraries/SwapUtils.sol:1039: for (uint256 i = 0; i < pooledTokens.length; i++) { core/connext/libraries/SwapUtils.sol:1055: for (uint256 i = 0; i < pooledTokens.length; i++) { core/connext/libraries/LibDiamond.sol:104: for (uint256 facetIndex; facetIndex < _diamondCut.length; facetIndex++) { core/connext/libraries/LibDiamond.sol:129: for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { core/connext/libraries/LibDiamond.sol:147: for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { core/connext/libraries/LibDiamond.sol:162: for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { core/connext/helpers/Multicall.sol:16: for (uint256 i = 0; i < calls.length; i++) { core/connext/helpers/StableSwap.sol:81: for (uint8 i = 0; i < _pooledTokens.length; i++) { core/connext/helpers/ConnextPriceOracle.sol:176: for (uint256 i = 0; i < tokenAddresses.length; i++) {
It is recommended to cache the array length on a variable before running the loop, then it doesn't need to read the length on every iteration, which cost gas, for example :
uint256 len = tokenAddresses.length; core/connext/helpers/ConnextPriceOracle.sol:176: for (uint256 i = 0; i < len; i++) {
Prefix increment ++i
returns the updated value after it's incremented and postfix increment i++
returns the original value then increments it. Prefix increment costs less gas compared to postfix increment.
core/relayer-fee/libraries/RelayerFeeMessage.sol:85: i++; core/connext/facets/RelayerFacet.sol:144: i++; core/connext/facets/RelayerFacet.sol:168: i++; core/connext/facets/BridgeFacet.sol:613: i++; core/connext/facets/BridgeFacet.sol:684: i++; core/connext/facets/BridgeFacet.sol:799: i++; core/connext/facets/DiamondLoupeFacet.sol:31: for (uint256 i; i < numFacets; i++) { core/connext/facets/StableSwapFacet.sol:415: for (uint8 i = 0; i < _pooledTokens.length; i++) { core/connext/libraries/SwapUtils.sol:205: for (uint256 i = 0; i < xp.length; i++) { core/connext/libraries/SwapUtils.sol:254: for (uint256 i = 0; i < numTokens; i++) { core/connext/libraries/SwapUtils.sol:268: for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { core/connext/libraries/SwapUtils.sol:289: for (uint256 i = 0; i < numTokens; i++) { core/connext/libraries/SwapUtils.sol:300: for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { core/connext/libraries/SwapUtils.sol:302: for (uint256 j = 0; j < numTokens; j++) { core/connext/libraries/SwapUtils.sol:344: for (uint256 i = 0; i < numTokens; i++) { core/connext/libraries/SwapUtils.sol:405: for (uint256 i = 0; i < numTokens; i++) { core/connext/libraries/SwapUtils.sol:425: for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { core/connext/libraries/SwapUtils.sol:558: for (uint256 i = 0; i < balances.length; i++) { core/connext/libraries/SwapUtils.sol:591: for (uint256 i = 0; i < balances.length; i++) { core/connext/libraries/SwapUtils.sol:844: for (uint256 i = 0; i < pooledTokens.length; i++) { core/connext/libraries/SwapUtils.sol:869: for (uint256 i = 0; i < pooledTokens.length; i++) { core/connext/libraries/SwapUtils.sol:924: for (uint256 i = 0; i < amounts.length; i++) { core/connext/libraries/SwapUtils.sol:1014: for (uint256 i = 0; i < pooledTokens.length; i++) { core/connext/libraries/SwapUtils.sol:1019: for (uint256 i = 0; i < pooledTokens.length; i++) { core/connext/libraries/SwapUtils.sol:1039: for (uint256 i = 0; i < pooledTokens.length; i++) { core/connext/libraries/SwapUtils.sol:1055: for (uint256 i = 0; i < pooledTokens.length; i++) { core/connext/libraries/LibDiamond.sol:104: for (uint256 facetIndex; facetIndex < _diamondCut.length; facetIndex++) { core/connext/libraries/LibDiamond.sol:129: for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { core/connext/libraries/LibDiamond.sol:134: selectorPosition++; core/connext/libraries/LibDiamond.sol:147: for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { core/connext/libraries/LibDiamond.sol:153: selectorPosition++; core/connext/libraries/LibDiamond.sol:162: for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { core/connext/helpers/Multicall.sol:16: for (uint256 i = 0; i < calls.length; i++) { core/connext/helpers/StableSwap.sol:81: for (uint8 i = 0; i < _pooledTokens.length; i++) { core/connext/helpers/ConnextPriceOracle.sol:176: for (uint256 i = 0; i < tokenAddresses.length; i++) {
It is recommended to use prefix increment instead of postfix one when the return value is not needed, as both of them will give the same result and prefix increment costs less gas.
For example :
core/connext/libraries/SwapUtils.sol:205: for (uint256 i = 0; i < xp.length; ++i) {
Shortening revert strings to fit in 32 bytes will decrease gas costs for deployment and gas costs when the revert condition has been met.
core/connext/helpers/OZERC20.sol:185: require(_sender != address(0), "ERC20: transfer from the zero address"); core/connext/helpers/OZERC20.sol:186: require(_recipient != address(0), "ERC20: transfer to the zero address"); core/connext/helpers/OZERC20.sol:205: require(_account != address(0), "ERC20: mint to the zero address"); core/connext/helpers/OZERC20.sol:226: require(_account != address(0), "ERC20: burn from the zero address"); core/connext/helpers/OZERC20.sol:253: require(_owner != address(0), "ERC20: approve from the zero address"); core/connext/helpers/OZERC20.sol:254: require(_spender != address(0), "ERC20: approve to the zero address"); core/connext/libraries/LibDiamond.sol:66: require(msg.sender == diamondStorage().contractOwner, "LibDiamond: Must be contract owner"); core/connext/libraries/LibDiamond.sol:121: require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut"); core/connext/libraries/LibDiamond.sol:123: require(_facetAddress != address(0), "LibDiamondCut: Add facet can't be address(0)"); core/connext/libraries/LibDiamond.sol:132: require(oldFacetAddress == address(0), "LibDiamondCut: Can't add function that already exists"); core/connext/libraries/LibDiamond.sol:139: require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut"); core/connext/libraries/LibDiamond.sol:141: require(_facetAddress != address(0), "LibDiamondCut: Add facet can't be address(0)"); core/connext/libraries/LibDiamond.sol:150: require(oldFacetAddress != _facetAddress, "LibDiamondCut: Can't replace function with same function"); core/connext/libraries/LibDiamond.sol:158: require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut"); core/connext/libraries/LibDiamond.sol:161: require(_facetAddress == address(0), "LibDiamondCut: Remove facet address must be address(0)"); core/connext/libraries/LibDiamond.sol:191: require(_facetAddress != address(0), "LibDiamondCut: Can't remove function that doesn't exist"); core/connext/libraries/LibDiamond.sol:193: require(_facetAddress != address(this), "LibDiamondCut: Can't remove immutable function"); core/connext/libraries/LibDiamond.sol:224: require(_calldata.length == 0, "LibDiamondCut: _init is address(0) but_calldata is not empty"); core/connext/libraries/LibDiamond.sol:226: require(_calldata.length > 0, "LibDiamondCut: _calldata is empty but _init is not address(0)"); core/connext/libraries/SwapUtils.sol:784: require(dy <= self.balances[tokenIndexTo], "Cannot get more than pool balance"); core/connext/libraries/SwapUtils.sol:823: require(amounts.length == pooledTokens.length, "Amounts must match pooled tokens"); core/connext/libraries/SwapUtils.sol:917: require(minAmounts.length == pooledTokens.length, "minAmounts must match poolTokens");
It is recommended to use error code and providing a reference to the error code instead of a long revert string., for example :
// Link to the reference of error codes core/connext/libraries/SwapUtils.sol:917: require(minAmounts.length == pooledTokens.length, "ERR");