Axelar Network v2 contest - kyteg's results

Decentralized interoperability network.

General Information

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

Axelar Network

Findings Distribution

Researcher Performance

Rank: 29/75

Findings: 2

Award: $88.39

🌟 Selected for report: 0

🚀 Solo Findings: 0

Summary

  • Low Risk Issues

      1. Use of deprecated transfer() when sending Ether
      1. Missing zero address check may lead to loss of Eth or ownership
  • Non-critical Issues

      1. public functions not called by the contract should be declared external instead

Low Risk Issues

1. Use of deprecated transfer() when sending Ether

transfer() uses a fixed 2300 gas. If the address where funds are transferred to uses more than this to handle the incoming Ether, the order transaction will revert. This limits the protocol to only interact with addresses where gas use upon receiving Ether is less than or equal to 2300 gas.

If for example, a refund was supposed to be given to a particular address, and if the address is a contract that implements logic requiring more than 2300 gas on receiving Ether, the refund will revert and the user will not receive the refund.

More information: https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/

Recommended mitigation:

Used call instead. For example:

    (bool success, ) = msg.sender.call{amount}("");
    require(success, "Transfer failed.");

There are 7 instances of this issue:

FILE: contracts/gas-service/AxelarGasService.sol 128: if (amount > 0) receiver.transfer(amount); 144: receiver.transfer(amount);

https://github.com/code-423n4/2022-07-axelar/blob/main/contracts/gas-service/AxelarGasService.sol#L128

FILE: contracts/deposit-service/ReceiverImplementation.sol 23: if (address(this).balance > 0) refundAddress.transfer(address(this).balance); 51: if (address(this).balance > 0) refundAddress.transfer(address(this).balance); 71: if (address(this).balance > 0) refundAddress.transfer(address(this).balance); 86: recipient.transfer(amount);

https://github.com/code-423n4/2022-07-axelar/blob/main/contracts/deposit-service/ReceiverImplementation.sol#L23

FILE: xc20/contracts/XC20Wrapper.sol 63: payable(msg.sender).transfer(address(this).balance);

https://github.com/code-423n4/2022-07-axelar/blob/main/xc20/contracts/XC20Wrapper.sol#L63

2. Missing zero address check may lead to loss of Eth or ownership

Missing zero address check may lead to a user accidentally transferring Ether to the 0 address, resulting in loss of funds.

Recommended mitigation:

Check to ensure the target address is not 0x0 before transfering Ether.

There are 3 instances of this issue:

FILE: contracts/deposit-service/ReceiverImplementaion.sol 23: if (address(this).balance > 0) refundAddress.transfer(address(this).balance); 71: if (address(this).balance > 0) refundAddress.transfer(address(this).balance); 86: recipient.transfer(amount);

https://github.com/code-423n4/2022-07-axelar/blob/main/contracts/deposit-service/ReceiverImplementation.sol#L23

Non-critical Issues

1. public functions not called by the contract should be declared external instead

Contracts are allowed to override their parents' functions and change the visibility from external to public if required.

There is 1 instance of this issue:

FILE: contracts/deposit-service/AxelarDepositService.sol 241: function contractId() public pure returns (bytes32) {

https://github.com/code-423n4/2022-07-axelar/blob/main/contracts/deposit-service/AxelarDepositService.sol#L241

#0 - re1ro

2022-08-05T05:10:00Z

1

Dup #4

2

Dup #3

3

Good spot

#1 - GalloDaSballo

2022-09-01T00:10:32Z

1. Use of deprecated transfer() when sending Ether

L

2. Missing zero address check may lead to loss of Eth or ownership

L

1. public functions not called by the contract should be declared external instead

NC

#2 - GalloDaSballo

2022-09-01T00:11:10Z

2L 1NC

Summary

IssueInstances
1. Use ++i instead of i++ to save gas4
2. Cache array length outside of loop7
3. Use != 0 instead of > 0 for a uint7
4. Let the default value be applied to variables initialized to the default value5
5. Return values directly without an intermediate return variable1

Gas Optimisations

1. Use ++i instead of i++ to save gas

This is especially relevant for the use of i++ in for loops. This saves 6 gas per loop.

See: https://github.com/byterocket/c4-common-issues/blob/main/0-Gas-Optimizations.md/#g012---use-prefix-increment-instead-of-postfix-increment-if-possible

There are 4 instances of this issue:

FILE: contracts/gas-service/AxelarGasService.sol 123: for (uint256 i; i < tokens.length; i++) {

https://github.com/code-423n4/2022-07-axelar/blob/main/contracts/gas-service/AxelarGasService.sol#L123

FILE: contracts/deposit-service/AxelarDepositService.sol 114: for (uint256 i; i < refundTokens.length; i++) { 168: for (uint256 i; i < refundTokens.length; i++) { 204: for (uint256 i; i < refundTokens.length; i++) {

https://github.com/code-423n4/2022-07-axelar/blob/main/contracts/deposit-service/AxelarDepositService.sol#L114

2. Cache array length outside of loop

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. To do this, create a variable containing the array length before the loop.

See: https://github.com/byterocket/c4-common-issues/blob/main/0-Gas-Optimizations.md/#g002---cache-array-length-outside-of-loop

_There are 7 instances of this issue:

FILE: contracts/gas-service/AxelarGasService.sol 123: for (uint256 i; i < tokens.length; i++) {

https://github.com/code-423n4/2022-07-axelar/blob/main/contracts/gas-service/AxelarGasService.sol#L123

FILE: contracts/deposit-service/AxelarDepositService.sol 114: for (uint256 i; i < refundTokens.length; i++) { 168: for (uint256 i; i < refundTokens.length; i++) { 204: for (uint256 i; i < refundTokens.length; i++) {

https://github.com/code-423n4/2022-07-axelar/blob/main/contracts/deposit-service/AxelarDepositService.sol#L114

FILE: contracts.auth/AxelarAuthWeighted.sol 17: for (uint256 i; i < recentOperators.length; ++i) { 98: for (uint256 i = 0; i < signatures.length; ++i) { 116: for (uint256 i; i < accounts.length - 1; ++i) {

https://github.com/code-423n4/2022-07-axelar/blob/main/contracts/auth/AxelarAuthWeighted.sol#L17

3. Use != 0 instead of > 0 for a uint

Since the integers are unsigned, != 0 and > 0 are equivalent. Using != 0 is 6 gas per instance cheaper than > 0

See: https://github.com/byterocket/c4-common-issues/blob/main/0-Gas-Optimizations.md/#g003---use--0-instead-of--0-for-unsigned-integer-comparison

There are 7 instances of this issue:

FILE: contracts/gas-service/AxelarGasService.sol 128: if (amount > 0) receiver.transfer(amount); 131: if (amount > 0) _safeTransfer(token, receiver, amount);

https://github.com/code-423n4/2022-07-axelar/blob/main/contracts/gas-service/AxelarGasService.sol#L128

FILE: contracts/deposit-service/AxelarDepositService.sol 165: if (addressForNativeDeposit(salt, refundAddress, destinationChain, destinationAddress).balance > 0 && msg.sender != refundAddress)

https://github.com/code-423n4/2022-07-axelar/blob/main/contracts/deposit-service/AxelarDepositService.sol#L165

FILE: contracts/deposit-service/ReceiverImplementation.sol 23: if (address(this).balance > 0) refundAddress.transfer(address(this).balance); 51: if (address(this).balance > 0) refundAddress.transfer(address(this).balance); 71: if (address(this).balance > 0) refundAddress.transfer(address(this).balance);

https://github.com/code-423n4/2022-07-axelar/blob/main/contracts/deposit-service/ReceiverImplementation.sol#L23

FILE: contracts/auth/AxelarAuthWeighted.sol 76: if (epochForHash[newOperatorsHash] > 0) revert SameOperators();

https://github.com/code-423n4/2022-07-axelar/blob/main/contracts/auth/AxelarAuthWeighted.sol#L76

4. Let the default value be applied to variables initialized to the default value

Letting the default value of 0, false be initialized to variables costs less gas compared to initializing it to these default values.

See: https://github.com/byterocket/c4-common-issues/blob/main/0-Gas-Optimizations.md/#g001---dont-initialize-variables-with-default-value

There are 5 instances of this issue:

FILE: contracts/auth/AxelarAuthWeighted.sol 68: uint256 totalWeight = 0; 69: for (uint256 i = 0; i < weightsLength; ++i) { 94: uint256 operatorIndex = 0; 95: uint256 weight = 0; 98: for (uint256 i = 0; i < signatures.length; ++i) {

https://github.com/code-423n4/2022-07-axelar/blob/main/contracts/auth/AxelarAuthWeighted.sol#L68

5. Return values directly without an intermediate return variable

Initializing a return variable for a function, then assigning a value to it requires more gas compared to simply returning the value, as long as the variable is not being used elsewhere in the function.

There is 1 instance of this issue:

FILE: contracts/auth/AxelarAuthWeighted.sol 26: function validateProof(bytes32 messageHash, bytes calldata proof) external view returns (bool currentOperators) {

https://github.com/code-423n4/2022-07-axelar/blob/main/contracts/auth/AxelarAuthWeighted.sol#L26

#0 - re1ro

2022-08-05T05:13:19Z

1 - 4

Dup #2

5

Good spot. Will change to the simple return and add comments of what happens

#1 - GalloDaSballo

2022-08-23T00:47:34Z

Less than 300 gas saved

#2 - GalloDaSballo

2022-08-23T00:50:01Z

Fyi no difference for 5 it's not valid

#3 - GalloDaSballo

2022-08-23T00:50:24Z

See benchmark

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.13;

import "../../lib/test.sol";
import "../../lib/Console.sol";

contract GasTest is DSTest {
    DeclaredReturn c0;
    DirectReturn c1;

    function setUp() public {
        c0 = new DeclaredReturn();
        c1 = new DirectReturn();
    }

    function testGas() public {
        c0.doTheThing();
        c1.doTheThing();
    }
}

contract DeclaredReturn {
    function doTheThing() external returns (uint value){
        uint value = 123;
    }
}

contract DirectReturn {
    function doTheThing() external returns (uint){
        uint value = 123;

        return value;
    }
}
<img width="737" alt="Screenshot 2022-08-23 at 02 50 17" src="https://user-images.githubusercontent.com/13383782/186044476-54c42259-7b1d-4137-b782-6e5e3e962aa2.png">
AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter