
Description
CATANA (Contract Assessment Through trAnsactioN replAy) is a testing framework that addresses one of the key challenges in the evolution of blockchain-based applications: how to safely upgrade smart contracts after deployment. While smart contracts are designed to be immutable, modern decentralized applications often rely on upgradeable architectures (e.g., proxy patterns) to enable evolution, bug fixes, or feature extensions without redeploying a new address. However, new contract versions should be released without introducing regressions or incompatibilites with dependent systems.
CATANA tackles this issue through a capture-replay testing strategy specifically designed for Proxy-based Upgradeable Smart Contracts (USCs). The tool captures real transactions and execution traces from an existing deployed USC and then replays them against an upgraded version. By comparing the outcomes of the original and replayed executions, CATANA detects behavioral divergences, which can be a clear symptom of future issues.
Resources
Catana Repository : link
The main CATANA repository hosts the full source code and documentation describing how to capture blockchain traces, replay them against upgraded contracts, and interpret comparison results.
Related Publications
- CATANA: Replay Testing for the Ethereum Blockchain M Barboni, GD Angelis, A Morichetta, A Polini IFIP International Conference on Testing Software and Systems, 257-265
- Wielding Blockchain Transactions for Capture-Replay Testing of Upgradeable Smart Contracts M Barboni, G De Angelis, A Morichetta, A Polini ACM Transactions on Internet Technology
Getting Started
- Install the dependencies: with
npm install. - Add your Infura and Etherscan API keys in
catana-config.js. - Add your project configuration in
catana-config.js. - If needed, add the correct compiler version in
hardhat-config.js.
Commands
| Command | Description | Usage | Example |
|---|---|---|---|
capture <nTx> [startBlock] | Extract a window of max nTx transactions executed on the Proxy, from an optional startBlock | $ npm start buildWindow | $ npm start buildWindow 10000 |
replay <strategy> | Replay transactions on the local USC (single txHash, all) | $ npm start replay <strategy> | $ npm start replay 0x078abc... |
clean | Clean the testing environment | $ npm start clean | $ npm start clean |
Project Structure
contracts/: source codes of the Proxy contract, and of the Local Logic contract under test (V2);contracts/deployed: source codes of the Proxy contract, and of the old Logic contract deployed on Mainnet (V1);hardhat.config.js: Hardhat configuration file;catana-config.js: Replay testing configuration file;
Configuration
The Catana configuration is specified in a catana-config.js file.
| Field | Description | Default Value |
|---|---|---|
catanaDir | name of the directory where the catana logs are saved | ./catana |
transactionsPath | path to the file containing the transactions to be replayed | ./catana/transactions/transactions.json |
DeployedSourcesDir | path to the folder where the sources of the deployed contracts will be stored | ./contracts/deployed |
UpgradedSourcesDir | path to the folder where the sources of the upgraded contracts (SUT) are stored | ./contracts |
ProxyPath | Local path to the source code of the Proxy contract | – |
UpgradedLogicPath | Local path to the source code of the upgraded Logic contract under test | – |
DeployedProxyAddr | Address of the Proxy contract deployed on the Mainnet | – |
DeployedLogicAddr | Address of the Logic contract (V1) deployed on the Mainnet | – |
stateVarsBlacklist | Blacklist for state variables to be ignored during Capture-Replay testing | ["__gap", "_gap"] |
INFURA_KEY | Infura api key | – |
ETHERSCAN_KE | Etherscan api key | – |
Here’s a simple example of catana-config.js.
module.exports = {
catanaDir: "./catana",
transactionsPath: "./catana/transactions/transactions.json",
DeployedSourcesDir: "./contracts/deployed",
UpgradedSourcesDir: "./contracts",
ProxyPath: "./contracts/CErc20Delegator.sol",
UpgradedLogicPath: "./contracts/CErc20Delegate.sol",
DeployedProxyAddr: "0x5d3a536e4d6dbd6114cc1ead35777bab948e3643",
DeployedLogicAddr: "0xa035b9e130F2B1AedC733eEFb1C67Ba4c503491F",
stateVarsBlacklist: ["__gap", "_gap", "myVar"],
INFURA_KEY: "your-infura-key",
ETHERSCAN_KEY: "your-etherscan-key"
}