Platform: Code4rena
Start Date: 29/07/2022
Pot Size: $50,000 USDC
Total HM: 6
Participants: 75
Period: 5 days
Judge: GalloDaSballo
Total Solo HM: 3
Id: 149
League: ETH
Rank: 25/75
Findings: 2
Award: $90.60
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: oyc_109
Also found by: 0x1f8b, 0x52, 0xNazgul, 0xSmartContract, 0xf15ers, 8olidity, Aymen0909, Bnke0x0, CertoraInc, Chom, CodingNameKiki, Deivitto, Dravee, ElKu, IllIllI, JC, Lambda, Noah3o6, NoamYakov, RedOneN, Respx, ReyAdmirado, Rohan16, Rolezn, Ruhum, Sm4rty, TomJ, Twpony, Waze, Yiko, __141345__, ajtra, apostle0x01, ashiq0x01, asutorufos, bardamu, benbaessler, berndartmueller, bharg4v, bulej93, c3phas, cccz, ch13fd357r0y3r, codexploder, cryptonue, cryptphi, defsec, djxploit, durianSausage, fatherOfBlocks, gogo, hansfriese, horsefacts, ignacio, kyteg, lucacez, mics, rbserver, robee, sashik_eth, simon135, sseefried, tofunmi, xiaoming90
59.3842 USDC - $59.38
This is probably an oversight since SafeERC20 was imported and safeTransfer() was used for ERC20 token transfers. Nevertheless, note that approve() will fail for certain token implementations that do not return a boolean value (). Hence it is recommend to use safeApprove().
contracts/deposit-service/AxelarDepositService.sol: L30, L38, L64
contracts/deposit-service/AxelarDepositService.sol:30: IERC20(wrappedTokenAddress).approve(gateway, amount); contracts/deposit-service/ReceiverImplementation.sol:38: IERC20(tokenAddress).approve(gateway, amount); contracts/deposit-service/ReceiverImplementation.sol:64: IERC20(wrappedTokenAddress).approve(gateway, amount);
Update to safeapprove in the function.
It is good to add a require() statement that checks the return value of token transfers or to use something like OpenZeppelin’s safeTransfer/safeTransferFrom unless one is sure the given token reverts in case of a failure. Failure to do so will cause silent failures of transfers and affect token accounting in contract.
contracts/gas-service/AxelarGasService.sol: L173 L128 L144
contracts/gas-service/AxelarGasService.sol:173: abi.encodeWithSelector(IERC20.transferFrom.selector, from, address(this), amount) contracts/gas-service/AxelarGasService.sol:128: if (amount > 0) receiver.transfer(amount); contracts/gas-service/AxelarGasService.sol:144: receiver.transfer(amount);
contracts/deposit-service/ReceiverImplementation.sol: L23 L51 L71 L86
contracts/deposit-service/ReceiverImplementation.sol:23: if (address(this).balance > 0) refundAddress.transfer(address(this).balance); contracts/deposit-service/ReceiverImplementation.sol:51: if (address(this).balance > 0) refundAddress.transfer(address(this).balance); contracts/deposit-service/ReceiverImplementation.sol:71: if (address(this).balance > 0) refundAddress.transfer(address(this).balance); contracts/deposit-service/ReceiverImplementation.sol:86: recipient.transfer(amount);
xc20/contracts/XC20Wrapper.sol: L63 L107
xc20/contracts/XC20Wrapper.sol:63: payable(msg.sender).transfer(address(this).balance); xc20/contracts/XC20Wrapper.sol:107: abi.encodeWithSelector(IERC20.transferFrom.selector, from, address(this), amount)
contracts/AxelarGateway.sol: L501 L523
contracts/AxelarGateway.sol:501: abi.encodeWithSelector(IERC20.transferFrom.selector, sender, address(this), amount) contracts/AxelarGateway.sol:523: IERC20.transferFrom.selector,
This similar medium-severity finding from Consensys Diligence Audit of Fei Protocol.
Consider using safeTransfer/safeTransferFrom or require() consistently.
Some tokens (like USDT L199) do not work when changing the allowance from an existing non-zero allowance value. They must first be approved by zero and then the actual allowance must be approved.
IERC20(token).approve(address(operator), 0); IERC20(token).approve(address(operator), amount);
contracts/deposit-service/AxelarDepositService.sol: L30, L38, L64
contracts/deposit-service/AxelarDepositService.sol:30: IERC20(wrappedTokenAddress).approve(gateway, amount); contracts/deposit-service/ReceiverImplementation.sol:38: IERC20(tokenAddress).approve(gateway, amount); contracts/deposit-service/ReceiverImplementation.sol:64: IERC20(wrappedTokenAddress).approve(gateway, amount);
Approve with a zero amount first before setting the actual amount.
Some tokens, such as Tether (USDT) return false rather than reverting if the approval fails. Use OpenZeppelin’s safeApprove(), which reverts if there’s a failure, instead
contracts/deposit-service/AxelarDepositService.sol: L30, L38, L64
contracts/deposit-service/AxelarDepositService.sol:30: IERC20(wrappedTokenAddress).approve(gateway, amount); contracts/deposit-service/ReceiverImplementation.sol:38: IERC20(tokenAddress).approve(gateway, amount); contracts/deposit-service/ReceiverImplementation.sol:64: IERC20(wrappedTokenAddress).approve(gateway, amount);
https://code4rena.com/reports/2022-05-sturdy#l-03-missing-checks-for-approves-return-status
Recommend using fixed solidity version
contracts/interfaces/IDepositBase.sol contracts/interfaces/IAxelarGasService.sol contracts/interfaces/IAxelarAuth.sol contracts/interfaces/IAxelarAuthWeighted.sol contracts/interfaces/IAxelarDepositService.sol
contracts/interfaces/IDepositBase.sol:3:pragma solidity ^0.8.9; contracts/interfaces/IAxelarGasService.sol:3:pragma solidity ^0.8.9; contracts/interfaces/IAxelarAuth.sol:3:pragma solidity ^0.8.9; contracts/interfaces/IAxelarAuthWeighted.sol:3:pragma solidity ^0.8.9; contracts/interfaces/IAxelarDepositService.sol:3:pragma solidity ^0.8.9;
If the intention is for the Ether to be used, the function should call another function, otherwise it should revert.
contracts/deposit-service/AxelarDepositServiceProxy.sol:13 contracts/deposit-service/DepositReceiver.sol:29
contracts/deposit-service/AxelarDepositServiceProxy.sol:13: receive() external payable override {} contracts/deposit-service/DepositReceiver.sol:29: receive() external payable {}
#0 - GalloDaSballo
2022-09-04T21:05:53Z
L
##Â 2. Use safeTransfer/safeTransferFrom consistently instead of transfer/transferFrom
Disputed, most of references are for payable.transfer
to send ETH
Disputed as all approves are then exhausted via a transferFrom
NC
L
2 L 1NC
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0xNazgul, 0xsam, 8olidity, Aymen0909, Bnke0x0, Chom, CodingNameKiki, Deivitto, Dravee, ElKu, Fitraldys, JC, Lambda, MiloTruck, Noah3o6, NoamYakov, RedOneN, Respx, ReyAdmirado, Rohan16, Rolezn, Ruhum, Sm4rty, TomJ, Tomio, Waze, __141345__, a12jmx, ajtra, ak1, apostle0x01, asutorufos, benbaessler, bharg4v, bulej93, c3phas, defsec, djxploit, durianSausage, erictee, fatherOfBlocks, gerdusx, gogo, kyteg, lucacez, medikko, mics, owenthurm, oyc_109, rbserver, robee, sashik_eth, simon135, tofunmi
31.222 USDC - $31.22
If a variable is not set/initialized, it is assumed to have the default value (0 for uint, false for bool, address(0) for address…). Explicitly initializing it with its default value is an anti-pattern and wastes gas.
We can use uint number;
instead of uint number = 0;
contracts/auth/AxelarAuthWeighted.sol:68 contracts/auth/AxelarAuthWeighted.sol:94 contracts/auth/AxelarAuthWeighted.sol:95
contracts/auth/AxelarAuthWeighted.sol:68: uint256 totalWeight = 0; contracts/auth/AxelarAuthWeighted.sol:94: uint256 operatorIndex = 0; contracts/auth/AxelarAuthWeighted.sol:95: uint256 weight = 0;
I suggest removing explicit initializations for default values.
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.
Links: contracts/gas-service/AxelarGasService.sol:123
contracts/gas-service/AxelarGasService.sol:123: for (uint256 i; i < tokens.length; i++) {
contracts/auth/AxelarAuthWeighted.sol: L17, L98, L116
contracts/auth/AxelarAuthWeighted.sol:17: for (uint256 i; i < recentOperators.length; ++i) { contracts/auth/AxelarAuthWeighted.sol:98: for (uint256 i = 0; i < signatures.length; ++i) { contracts/auth/AxelarAuthWeighted.sol:116: for (uint256 i; i < accounts.length - 1; ++i) {
contracts/AxelarGateway.sol:207
contracts/AxelarGateway.sol:207: for (uint256 i = 0; i <symbols.length; i++) {
ontracts/deposit-service/AxelarDepositService.sol: L114 L186 L204
contracts/deposit-service/AxelarDepositService.sol:114: for (uint256 i; i < refundTokens.length; i++) { contracts/deposit-service/AxelarDepositService.sol:168: for (uint256 i; i < refundTokens.length; i++) { contracts/deposit-service/AxelarDepositService.sol:204: for (uint256 i; i < refundTokens.length; i++) {
Here, I suggest storing the array’s length in a variable before the for-loop, and use it instead.
contracts/auth/AxelarAuthWeighted.so;: 70 , 105
contracts/auth/AxelarAuthWeighted.sol:70: totalWeight += newWeights[i]; contracts/auth/AxelarAuthWeighted.sol:105: weight += weights[operatorIndex];
https://github.com/code-423n4/2022-05-backd-findings/issues/108
#0 - GalloDaSballo
2022-08-23T01:12:04Z
Less than 100 gas saved (no unchecked nor ++i)