Overview
This guide covers testing strategies for EVM CCTP Contracts, including unit tests, integration tests, linting, and static analysis.
Prerequisites
- Foundry CLI installed (forge 0.2.0)
- Git submodules initialized:
git submodule update --init --recursive
- Yarn installed:
yarn install
Unit Tests
Running Tests
Run unit tests using the Foundry forge CLI:
Alternatively, run tests in a Docker container:
Test Output
Successful test output:
[PASS] testDepositForBurn() (gas: 123456)
[PASS] testReceiveMessage() (gas: 234567)
Test result: ok. 42 passed; 0 failed; finished in 2.34s
Debug Logs
Control test verbosity using the -v flag to display logs and traces:
Verbosity Levels
| Flag | Output Level | Description |
|---|
-v | Basic | Shows test results |
-vv | Detailed | Shows console.log() statements |
-vvv | Verbose | Shows execution traces |
-vvvv | Very Verbose | Shows execution traces with stack |
-vvvvv | Maximum | Shows all execution details |
Examples
Display console.log statements:
Show full execution traces:
Test specific contract:
forge test --match-contract MessageTransmitterTest -vv
Test specific function:
forge test --match-test testDepositForBurn -vvv
Adding Debug Logs
To use console.log() in your contracts, import the console library:
import "lib/forge-std/src/console.sol";
contract MyContract {
function myFunction() public {
console.log("Debug value:", someValue);
}
}
Integration Tests
Integration tests run against a local Anvil test node and simulate full cross-chain scenarios.
Running Integration Tests
This command:
- Starts an Anvil test node in a Docker container
- Deploys all contracts
- Runs integration test suite
- Cleans up resources
Example Integration Test
An example integration test is available in the anvil/ folder that demonstrates:
- Deploying contracts to local testnet
- Configuring cross-chain messaging
- Executing full deposit and burn flow
- Simulating attestation
- Receiving messages on destination chain
Manual Integration Testing
Start Anvil manually for interactive testing:
# Start Anvil node
anvil
# In another terminal, deploy contracts
forge script scripts/v1/deploy.s.sol --rpc-url http://localhost:8545 --broadcast
# Run your tests
forge test --fork-url http://localhost:8545
Linting
Lint Solidity code to enforce style guidelines and catch common issues:
This checks all .sol files in the src/ and test/ directories.
Linting Configuration
Linting rules are defined in .solhint.json. Common checks include:
- Code style and formatting
- Best practice violations
- Security anti-patterns
- Gas optimization opportunities
Fixing Linting Issues
Some linting issues can be auto-fixed:
Common Linting Rules
Naming Conventions:
// Bad
function MyFunction() public {}
// Good
function myFunction() public {}
Visibility Modifiers:
// Bad
function transfer() {}
// Good
function transfer() external {}
Import Organization:
// Good - imports at top, organized
import "./interfaces/IReceiver.sol";
import "./libraries/Message.sol";
Static Analysis
Run static analysis using Mythril to detect security vulnerabilities:
Analyzing Contracts
MessageTransmitter:
make analyze-message-transmitter
MessageTransmitterV2:
make analyze-message-transmitter-v2
TokenMessengerMinter:
make analyze-token-messenger-minter
Analyzing Individual Files
If Mythril is already installed:
myth -v4 analyze $FILE_PATH \
--solc-json mythril.config.json \
--solv 0.7.6
Example:
myth -v4 analyze src/MessageTransmitter.sol \
--solc-json mythril.config.json \
--solv 0.7.6
Static analysis can take several minutes to complete, especially for complex contracts.
Understanding Results
Mythril reports potential issues with severity levels:
- High: Critical security vulnerabilities
- Medium: Significant issues that should be addressed
- Low: Minor issues or informational warnings
Continuous Integration
GitHub Actions automatically runs tests on every push and pull request.
CI Workflow
The workflow configuration is in .github/workflows/ci.yml and includes:
- Linting checks
- Unit tests
- Integration tests
- Code coverage reports
Viewing CI Results
Check CI status:
- Navigate to the repository on GitHub
- Click the “Actions” tab
- Select the workflow run to view details
Local CI Simulation
Run the same checks locally before pushing:
# Run all checks
yarn lint && forge test && make anvil-test
Security Scanning
Olympix AI Scanning
Manually trigger Olympix.ai security scanning:
Navigate to Actions
Click on the “Actions” tab in your GitHub repository
Select Olympix Scan
In the left sidebar, select “Olympix Scan”
Run Workflow
Select the branch and click “Run workflow”
Review Results
Wait for the scan to complete and review security alerts
Test Coverage
Generate test coverage reports:
View detailed coverage:
forge coverage --report lcov
Coverage Goals
- Critical paths: 100% coverage
- Core contracts: >95% coverage
- Utility functions: >90% coverage
- Overall project: >90% coverage
Testing Best Practices
Write Comprehensive Tests
function testDepositForBurn() public {
// Setup
uint256 amount = 1000e6;
// Execute
uint64 nonce = tokenMessenger.depositForBurn(
amount,
destinationDomain,
recipient,
token
);
// Assert
assertEq(nonce, 1);
assertEq(usdc.balanceOf(address(this)), 0);
}
Test Edge Cases
- Zero amounts
- Maximum values
- Invalid addresses
- Unauthorized callers
- Paused state
- Reentrancy scenarios
Use Fuzz Testing
function testFuzz_depositForBurn(uint256 amount) public {
vm.assume(amount > 0 && amount <= maxAmount);
tokenMessenger.depositForBurn(
amount,
destinationDomain,
recipient,
token
);
// Assertions
}
Mock External Dependencies
function setUp() public {
// Deploy mocks
mockAttester = new MockAttester();
// Deploy contracts with mocks
messageTransmitter = new MessageTransmitter(
address(mockAttester)
);
}
Troubleshooting
Tests Fail to Compile
- Check Solidity version (should be 0.7.6)
- Verify all dependencies are installed
- Run
git submodule update --init --recursive
Tests Timeout
- Increase timeout in foundry.toml
- Check for infinite loops in contracts
- Verify RPC connection is stable
Coverage Reports Empty
- Ensure tests are passing first
- Check that test files are in
test/ directory
- Verify forge coverage is configured correctly
Next Steps