Platform: Code4rena
Start Date: 29/07/2022
End Date: 03/08/2022
Period: 5 days
Status: Completed
Pot Size: $50,000 USDC
Participants: 75
Reporter: liveactionllama
Judge: GalloDaSballo
Id: 149
League: ETH
Chom | 1/75 | $11,259.63 | 4 | 0 | 0 | 2 | 1 | - | - | 0 |
xiaoming90 | 2/75 | $7,836.03 | 2 | 0 | 0 | 1 | 1 | - | 0 | 0 |
__141345__ | 3/75 | $7,787.96 | 3 | 0 | 0 | 1 | 1 | - | - | 0 |
Lambda | 4/75 | $7,710.47 | 5 | 0 | 0 | 3 | 0 | - | - | 0 |
Ruhum | 5/75 | $2,169.87 | 3 | 0 | 0 | 1 | 0 | - | - | 0 |
Respx | 6/75 | $2,166.27 | 3 | 0 | 0 | 1 | 0 | - | - | 0 |
0x52 | 7/75 | $2,148.87 | 2 | 0 | 0 | 1 | 0 | - | 0 | 0 |
cryptphi | 8/75 | $2,134.93 | 2 | 0 | 0 | 1 | 0 | - | 0 | 0 |
oyc_109 | 9/75 | $608.60 | 2 | 0 | 0 | 0 | 0 | - | - | 0 |
IllIllI | 10/75 | $529.69 | 2 | 0 | 0 | 0 | 0 | - | - | 0 |
Auditor per page
Axelar is a decentralized interoperability network connecting all blockchains, assets and apps through a universal set of protocols and APIs. It is built on top off the Cosmos SDK. Users/Applications can use Axelar network to send tokens between any Cosmos and EVM chains. They can also send arbitrary messages between EVM chains.
Axelar network's decentralized validators confirm events emitted on EVM chains (such as deposit confirmation and message send), and sign off on commands submitted (by automated services) to the gateway smart contracts (such as minting token, and approving message on the destination).
This will be the second contest organized by Axelar. The scope of this contest covers some crucial changes made to the protocol (for the gateway contracts) compared to the last contest version, but otherwise focuses on various service and application contracts that sit on top of the protocol.
The contest will focus on the following:
xc20
subfolder, with a separate build system.Note: Known issues/PRs posted on GitHub aren't considered valid findings:
Furthermore, any findings from the prior contest aren't considered applicable to avoid duplicates. Some of the prior findings might have not been addressed yet due to either being low priority, or deemed not a concern, or designed intentionally, etc. by the team. The prior findings were about the gateway contract, so there shouldn't be any overlap with the new service contracts.
npm ci npm run build npm run test
Gas costs: npm run test
prints gas costs of various methods.
For XC20Wrapper.sol
, first cd xc20
, and then build/test as above. To create your own local deployment, follow the steps below.
Add a string for PRIVATE_KEY_GENERATOR
to .env
to get your own private key.
To create a local Moonbeam (on a separate terminal):
node scripts/createLocal
To deploy the contracts:
node scripts/deploy local
To test the contracts:
node scripts/addLocalXc20.js local node scripts/addWrapping local aUSDC
See test.js for more in-depth tests.
AxelarGateway.execute()
takes a signed batched of commands.
Each command has a corresponding commandID
. This is guaranteed to be unique from the Axelar network. execute
intentionally allows retrying
a commandID
if the command
failed to be processed; this is because commands are state dependent, and someone might submit command 2 before command 1 causing it to fail.External
: An external ERC-20 token on it's native chain is registered as external, e.g. USDC
on Ethereum.InternalBurnableFrom
: Axelar wrapped tokens that are minted by the Axelar network when transferring over the original token, e.g. axlATOM
, axlUSDC
on Avalanche.InternalBurnable
: v1.0.0
version of Axelar wrapped tokens that used a different deposit address contract, e.g. UST
(native to Terra) on Avalanche.
New tokens cannot be of this type, and this is only present for legacy support.TokenDeployer
contract.AxelarAuthWeighted
contract.AxelarGateway
contract with the auth weighted and token deployer address.AxelarGatewayProxy
contract with the implementation contract address (from above) and setup
params obtained
from the current network state (current set of operators).A
is deployed (AxelarGateway.deployToken()
)
on each non-native EVM chain as an ERC-20 token (BurnableMintableCappedERC20.sol
).DepositHandler.sol
is deployed,
BurnableMintableCappedERC20.depositAddress()
) on source EVM chain.A
at that address, and the deposit contract locks the token at the gateway (or burns them for wrapped tokens).Transfer
event using their RPC nodes for the source chain (using majority voting).AxelarGateway.execute()
.mint
's the specified amount of the wrapped Token A
to the destination address.AxelarDepositService.addressForNativeUnwrap()
.AxelarDepositService.addressForTokenDeposit()
with using the un-wrap address as a destination.AxelarDepositService.sendTokenDeposit()
.AxelarDepositService
deploys DepositReceiver
to that generated address which will call AxelarGateway.sendToken()
.TokenSent
event.AxelarDepositService.nativeUnwrap()
.AxelarDepositService
deploys DepositReceiver
which will call IWETH9.withdraw()
and transfer native currency to the recipient address.IAxelarExecutable.sol
interface to receive the message.ERC20.approve()
beforehand to allow the gateway contract
to transfer the specified amount
on behalf of the sender/source contract.AxelarGateway.callContractWithToken()
with the destination chain/address, payload
and token.AxelarGasService.payNativeGasForContractCallWithToken
method on the AxelarGasServiceProxy
contract
to pay in the native gas token the fees for relaying the call approval to axelar's gateway, and to execute the remote contract.
Axelar's microservices monitor the emitted events, and relay the call approval if sufficient gas was paid.payload
in a regular database, keyed by the hash(payload)
, that anyone can query by.ContractCallWithToken
event.AxelarGateway.approveContractCallWithMint()
command, signed by the validators.payload hash
and emits the event ContractCallApprovedWithMint
.IAxelarExecutable.executeWithToken()
on the destination contract, with the payload
and other data as params.executeWithToken
of the destination contract verifies that the contract call was indeed approved by calling AxelarGateway.validateContractCallAndMint()
on the gateway contract.payload
for it's own application.See this example cross-chain NFT application.
The following contracts are in-scope for the audit. The remaining code in the repo is only relevant for tests, utils, samples etc., and not in scope.
Here's the list of in-scope files. Summaries are provided in the following section.
Type | File | Lines | Capabilities |
---|---|---|---|
Contracts | AxelarGateway (partly in scope) | 658 | DelegateCall, Uses Hash Functions, TryCatch |
Interfaces | IDepositBase | 21 | |
Contracts | AxelarGasService | 183 | Payable, Transfers ETH, Uses Hash Functions |
Contracts | AxelarGasServiceProxy | 12 | Uses Hash Functions |
Interfaces | IAxelarAuth | 11 | |
Interfaces | IAxelarGasService | 124 | Payable |
Interfaces | IAxelarDepositService | 79 | Payable |
Interfaces | IAxelarAuthWeighted | 21 | |
Contracts | DepositReceiver | 30 | Uses Assembly, Payable, Has Destroyable Contracts, DelegateCall |
Contracts | AxelarDepositService | 244 | Payable, Uses Hash Functions, New/Create/Create2 |
Contracts | DepositBase | 76 | Uses Assembly |
Contracts | ReceiverImplementation | 88 | Transfers ETH |
Contracts | AxelarDepositServiceProxy | 14 | Payable, Uses Hash Functions |
Contracts | AxelarAuthWeighted | 124 | Uses Hash Functions |
Contracts | XC20Wrapper | 128 | Payable, Transfers ETH, Uses Hash Functions |
Contracts, Interfaces | Totals | 1813 | Uses Assembly, Payable, Has Destroyable Contracts, Transfers ETH, DelegateCall, Uses Hash Functions, New/Create/Create2, TryCatch |
Interface for the wrapped ERC-20 version of the native gas token, e.g. WETH. This is used to wrap/unwrap native gas token.
This interface needs to be implemented by the application contract to receive cross-chain messages. See the token swapper example for an example.
Contracts that are in scope are tagged as such. Other contracts are mentioned for completeness due to being dependencies.
Our gateway contracts implement the proxy pattern to allow upgrades.
Calls are delegated to the implementation contract while using the proxy's storage.
setup
is overridden to be an empty method on the proxy contract to prevent anyone besides the proxy contract from calling the implementation's setup
on the proxy storage.
The implementation contract that accepts commands signed by Axelar network's validators/operators (see execute
).
The setup
and upgrade
are not callable on the implementation contract by anyone.
Note: The part of this contract that is in scope is the execute
method and transferOperatorship
(i.e interactions with the auth contract, ignore the other commands).
The auth contract that verifies that commands are signed by a weighted set of operator keys. It also performs transfers of operatorships (to mimic changes to the validator set of Axelar Proof-of-Stake network).
The deposit service contract that allows a user to send their ERC20 or native token to a deposit address controlled by the service which can be forwarded to the Axelar gateway for a cross-chain transfer. It also wraps native token, such as ETH, to an ERC-20 token, WETH, as the Axelar Gateway only supports ERC-20 tokens.
This is the base deposit service contract that is inherited by the AxelarDepositService.sol
and ReceiverImplementation.sol
.
Proxy contract for the deposit service.
This contract is deployed by the AxelarDepositService.sol
to act as the recipient address for the cross-chain transfer.
When tokens arrive here, it calls the ReceiverImplementation.sol
method to forward the tokens to the user, auto-unwrapping if necessary.
The receiver contract that accepts tokens and forwards them to the user address. It also unwraps wrapped native tokens like WETH, so the user gets the native token instead.
This contract accepts gas payments from applications who wish to use Axelar's microservices
that relay the contract calls made by the application using the Axelar gateway.
Gas payment should be made in the same tx as the callContract
on the Axelar gateway for the
microservices to consider it. The gas service allows refunds for any excessive gas payments
after the remote contract has been called with the payload. The application can pay in the native gas token
or a supported ERC20 token. If for some reason, the gas payment was insufficient, the application can pay
more via addGas
/addNativeGas
methods for the appropriate tx hash.
Proxy contract for the gas service.
This contract is present in the xc20
subfolder.
Moonbeam, an EVM compatible chain in the Polkadot ecosystem, supports ERC20-compatible tokens called XC20. These function like normal ERC20s but can also be sent to the rest of Polkadot through special non-EVM transactions on Moonbeam. You can get new XC20s that an owner address is allowed to mint and burn through the Moonbeam consensus, and Moonbeam has agreed to provide some to bridge Axelar Tokens to the rest of Polkadot.
The XC20Wrapper.sol
is a contract that will be deployed on Moonbeam and can wrap Axelar Tokens into XC20s that are set as wrapped versions of those tokens.
First, for new XC20s we need to tell the XC20Wrapper what they wrap as well as set up their name and symbol.
Afterwards they can be wrapped and unwrapped freely by anyone, even remotely.
XC20Sample.sol
is a sample XC20 token for reference used in tests.
Inheritable proxy contract
Inheritable upgradable implementation contract
Multisig governance contract. Upgrading the implementation is done via voting on the new implementation address from admin accounts.
Base ERC20 contract used to deploy wrapped version of tokens on other chains.
Allow an account to issue a spending permit to another account.
Mintable ERC20 token contract with an optional capped total supply (when capacity != 0
).
It also allows us the owner of the ERC20 contract to burn tokens for an account (IERC20BurnFrom
).
The main token contract that's deployed for Axelar wrapped version of tokens on non-native chains.
This contract allows burning tokens from deposit addresses generated (depositAddress
) by the Axelar network, where
users send their deposits. salt
needed to generate the address is provided in a signed burn command
from the Axelar network validators.
When the Axelar network submits a signed command to deploy a token,
the token deployer contract is called to deploy the BurnableMintableCappedERC20
token.
This is done to reduce the bytecode size of the gateway contract to allow deploying on EVM chains
with more restrictive gas limits.
The contract deployed at the deposit addresses that allows burning/locking of the tokens
sent by the user. It prevents re-entrancy, and while it's methods are permissionless,
the gateway deploys the deposit handler and burns/locks in the same call (see _burnToken
).
Define ownership of a contract and modifiers for permissioned methods.
Storage contract for the proxy.
Modified version of OpenZeppelin ECDSA signature authentication check.
We'd like wardens to particularly focus on the following:
execute
and transferOperatorship
in AxelarGateway.sol
which calls validateProof
on the AxelarAuthWeighted.sol
.Contracts repo: https://github.com/axelarnetwork/axelar-cgp-solidity
XC20 wrapper repo: https://github.com/axelarnetwork/axelar-xc20-wrapper
Network resources: https://docs.axelar.dev/resources
Token transfer app: https://satellite.money/
General Message Passing Usage: https://docs.axelar.dev/dev/gmp-overview
Example token transfer flow: https://docs.axelar.dev/learn/cli/axl-to-evm
Deployed contracts: https://docs.axelar.dev/dev/build/contract-addresses/mainnet
EVM module of the Axelar network that prepares commands for the gateway: https://github.com/axelarnetwork/axelar-core/blob/main/x/evm/keeper/msg_server.go
Multisig module of the Axelar network that signs commands for the gateway via the validators: https://github.com/axelarnetwork/axelar-core/blob/main/x/multisig/keeper/msg_server.go
### Do you have a link to the repo that the contest will cover? [https://github.com/axelarnetwork/axelar-cgp-solidity ### How many (non-library) contracts are in the scope? 8 ### Total sLoC in these contracts? 1057 ### How many library dependencies? 2 ### How many separate interfaces and struct definitions are there for the contracts within scope? 8 ### Does most of your code generally use composition or inheritance? Yes ### How many external calls? 0 ### Is there a need to understand a separate part of the codebase / get context in order to audit this part of the protocol? false ### Does it use an oracle? false ### Does the token conform to the ERC20 standard? N/A ### Are there any novel or unique curve logic or mathematical models? No ### Does it use a timelock function? No ### Is it an NFT? No ### Does it have an AMM? No ### Is it a fork of a popular project? false ### Does it use rollups? false ### Is it multi-chain? true ### Does it use a side-chain? false