Building projects on top of cross-chain solutions like Axelar can sometimes be tricky and may lead to security issues in the code. Fortunately, open-source tools can automatically detect many issues or help test the implementation.

Static analysis tools

Static analysis tools can detect vulnerabilities and code quality issues without executing the code. A tool typically implements detectors that often use heuristics to perform the analysis. There are a few static analysis tools for Solidity. We will demonstrate further the most well-known tool Slither and an alternative tool Woke, that implements Axelar-specific detectors.

Slither

The Slither tool can be installed with Python’s integrated installer using:

pip3 install slither-analyzer

It offers numerous detectors (80 at the time of writing). Nevertheless, many of the detectors are not relevant to the latest versions of Solidity, and some of the detectors are only informative (e.g. an inline assembly block is used). The tool can be run using the following command:

slither project-directory

Slither usually prints out a lot of information that is not very well formatted.

There is also a Slither extension for VS Code, but it requires the developer to run the detectors manually.

Woke

Woke is a development and testing framework for Solidity developed by Ackee Blockchain. Like Slither, Woke can perform static analysis using a prepared set of detectors, with detectors written especially for projects built on Axelar. Woke can be installed using the following command:

pip3 install woke

The number of detectors is limited, the tool does not aim to catch all possible issues, but a few specific ones. More detectors are being actively developed; all are relevant to the latest versions of Solidity, and it prints fewer false positive detections. Analysis of a project can be performed with the following command:

woke detect project-directory

Among other detectors, there is also a detector that checks whether Axelar Proxy/Upgradeable contracts are used in the correct way. Specifically, it checks that:

  • There is no more than one proxy contract with the same contractId.
  • There is no more than one upgradeable contract with the same contractId.
  • For every proxy contract, there is an upgradeable contract with the same contractId.
  • For every upgradeable contract, there is a proxy contract with the same contractId.
  • For each pair of matching proxy and upgradeable contract, there is no collision of function selectors (except for functions setup and implementation where this behavior is desirable).

The last point ensures that when a user calls a function of an upgradeable contract with a given selector through a proxy, the function is actually called on the upgradeable contract. If there were a function with the same selector on the proxy contract, it would be called instead. Usually, this is not the intended behavior.

 

The situation is illustrated in the following hypothetical scenario. Both functions proxyOwner and clash550254402 share the same selector. A user wants to execute the clash550254402 function, but the proxyOwner function is run instead. This makes it effectively impossible to run the clash550254402 function through the proxy. It is still possible to call the function directly on the upgradeable contract, but without the proxy contract context (storage, balance, etc.) it will likely behave differently.

Re-entrancy detector that can recognize an onlyOwner pattern and exclude re-entrancies that can only be executed by the owner of a contract. For each printed detection, there is a possible unsafe external call and a list of public/external functions from where the attack can be started.

For VS Code users, there is an extension named Tools for Solidity, which uses Woke in the background and offers many language server functionalities, such as:

  • Go to definition
  • Find all references
  • Hover
  • Code lens with a number of references and single-click labels to generate inheritance and control flow graphs

Vulnerability detector results are visualized directly in the editor and refreshed after every code change.

Static analysis tools can often warn of a possible issue in the code but can also report many false positive detections. A different approach is to test the implementation with simple unit tests or more sophisticated randomly generated test suits.

Dynamic analysis

Many testing frameworks allow writing tests in Javascript, Python, or even Solidity. One common problem when testing a cross-chain project is that it is usually needed to simulate chain relayers or test the implementation on a testnet. Fortunately, Axelar offers its development environment called Axelar Sandbox. This environment offers editors for Solidity and Javascript and includes ready-made EVM-compatible blockchains, Axelar Gateway and Axelar Relayers.

It is often satisfactory to emulate Axelar Gateway communication on a single chain. The Ackee-Blockchain/axelar-gateway-mock repository contains a custom implementation of Axelar Gateway designed to be used on a single development chain. The AxelarGatewayMock contract does not implement all of the IAxelarGateway interface functions, but it has implemented the sendToken, callContract, and callContractWithToken functions needed to support all types of communication.

 

An example use of the contract is given in the tests/test_gateway_mock.py file. It is written using the Woke testing framework, but the syntax should be easy to understand and helpful as a template for using the AxelarGatewayMock contract with different testing frameworks.

First, Axelar Gateways must be deployed and each gateway must register other gateways.

When working with tokens (sendToken and callContractWithToken functions), it is also necessary to deploy a token instance for each Axelar Gateway and register the instances at respective gateways.

AxelarGatewayMock expects ERC20 tokens to implement the mint and burn functions with the following signature:

It is then possible to call the sendToken, callContract, and callContractWithToken functions either directly from an externally owned account or via a contract. Full examples are available in the repository.

Summary

Testing cross-chain projects has its own pitfalls, but the tools presented can help catch common bugs. Both static and dynamic analysis tools should be used to cover different types of issues. Re-entrancy issues typically cannot be discovered using dynamic analysis tools, while checking project-specific invariants (such as the total amount of minted tokens at a given time) cannot be done with static analysis detectors. 

In addition to in-house testing, it is highly recommended to perform periodic audits. While nothing can guarantee 100% safety, using the proper tools along with periodic audits can greatly reduce the likelihood of a project being vulnerable to a serious issue.