Introduction
Project Leviathan is a deterministic, production-grade code migration tool for Web3 projects. It transforms legacy codebases from Brownie, Ethers v5, and Web3.js v1 to modern frameworks in seconds—not hours.
Philosophy: Zero false positives. We trace declarations back to the source and only transform what we can verify.
Why Leviathan?
- Deterministic: AST-based transformations, no guesswork
- Verified: Syntax validation after every migration
- Fast: 12+ hour manual migrations complete in under 4 seconds
- Safe: Type-aware transformations with fallback to skip uncertain changes
Architecture
Leviathan consists of three core modules orchestrated by a workflow engine:
- Web3.js v4 Evolution — JavaScript/TypeScript migrations
- Ethers v6 Bridge — BigNumber to BigInt transformations
- Brownie to Ape — Python smart contract migrations
Quick Start
Get up and running with Leviathan in minutes.
Prerequisites
- Node.js v18+
- Python 3.10+ (for Brownie/Ape modules)
- npm or yarn
Installation
# Clone the repository
git clone https://github.com/your-org/project-leviathan.git
cd project-leviathan
# Install dependencies
npm install
# Install the codemod CLI globally
npm install -g codemod
Run a Migration
# Run the smoke test on a sample project
npm run smoke-test
# Validate JavaScript files
npm run validate:js
# Validate Python files
npm run validate:py
Codemod CLI
The Codemod CLI is the core engine behind Leviathan. It uses AST-grep for deterministic pattern matching and transformation.
Commands
# Run a single codemod
codemod run <module-path>
# Run a workflow
codemod workflow run -w workflow.yaml
# Publish a module to the registry
codemod publish .
Options
--no-interactive— Run without prompts--allow-dirty— Allow running on uncommitted changes--dry-run— Preview changes without applying
Module Structure
modules/
├── web3js-v4-evolution/
│ ├── codemod.yaml # Module manifest
│ ├── workflow.yaml # Local workflow
│ ├── scripts/
│ │ └── codemod.ts # Transformation script
│ ├── rules/
│ │ └── config.yml # AST-grep rules
│ └── tests/
│ ├── test.ts
│ └── fixtures/
│ ├── input.js
│ └── expected.js
Writing a Codemod
Codemods are written in TypeScript and use the AST-grep API:
import { Codemod, File } from '@codemod/core';
export const codemod: Codemod = {
name: 'web3-migration',
description: 'Migrate Web3.js v1 to v4',
run(files: File[]) {
return files.map(file => {
return file.ast.update((node: any) => {
// Transform: require('web3') -> import { Web3 } from 'web3'
if (node.type === 'CallExpression' &&
node.callee.name === 'require' &&
node.arguments[0]?.value === 'web3') {
return {
type: 'ImportDeclaration',
specifiers: [{
type: 'ImportSpecifier',
imported: { type: 'Identifier', name: 'Web3' }
}],
source: { type: 'Literal', value: 'web3' }
};
}
return node;
});
});
}
};
Pro Tip: Always include test fixtures (input and expected output) to verify your codemod works correctly.
Workflow Engine
The workflow engine orchestrates multiple codemods in sequence with dependency resolution.
Workflow YAML
version: "1"
name: "Leviathan Migration"
nodes:
# Phase 1: Web3.js Migration
- id: web3js-migration
name: Web3.js v4 Evolution
type: automatic
steps:
- js-ast-grep:
js_file: "modules/web3js-v4-evolution/scripts/codemod.ts"
language: "javascript"
# Validation after each phase
- id: validate-web3
name: Validate Web3 Migration
type: automatic
depends_on: [web3js-migration]
steps:
- run: "npm run validate:js"
# Phase 2: Ethers v6 (runs after validation)
- id: ethers-v6-migration
name: Ethers.js v6 Bridge
type: automatic
depends_on: [validate-web3]
steps:
- js-ast-grep:
js_file: "modules/ethers-v6-bridge/scripts/codemod.ts"
Node Types
automatic— Runs without user inputmanual— Requires user confirmationconditional— Runs based on conditions
Core Modules
1. Web3.js v4 Evolution
Migrates legacy Web3.js v1 patterns to modern v4 syntax.
- Named exports transformation
- Constructor enforcement
- Event signature healing
- Provider pattern updates
2. Ethers v6 Bridge
Transforms Ethers.js v5 BigNumber methods to native BigInt operators.
- .add() → +
- .sub() → -
- .mul() → *
- .div() → /
- .eq() → ==
3. Brownie to Ape
Converts Python smart contract projects from Brownie to Ape Framework.
- Import transformations
- Account management migration
- Contract deployment patterns
- Testing framework conversion
Configuration
codemod.yaml
schema_version: "1.0"
name: "web3js-v4-evolution"
version: "0.1.0"
description: "Migrate Web3.js v1 legacy patterns to v4"
author: "Skywalkingzulu1"
license: "Apache-2.0"
workflow: "workflow.yaml"
targets:
languages: ["typescript", "javascript"]
keywords: ["web3", "blockchain", "ethereum"]
AST-grep Rules (Python)
rules:
- id: brownie-imports
pattern: from brownie import $IMPORTS
message: "Migrate Brownie imports to Ape"
transform:
- from: brownie
to: ape
- map:
accounts: accounts
network: network
project: project
Validation
Every transformation is validated to ensure syntax integrity.
JavaScript Validation
// validate.js
function validateJs() {
const { execSync } = require('child_process');
try {
execSync('node --check ' + file, { stdio: 'pipe' });
return true;
} catch (e) {
console.error('Syntax error in:', file);
return false;
}
}
Python Validation
# validate.py
import py_compile
import sys
def validate_python(file):
try:
py_compile.compile(file, doraise=True)
return True
except py_compile.PyCompileError as e:
print(f"Syntax error in {file}: {e}")
return False
Important: The workflow stops if validation fails, ensuring no broken code reaches your repository.
Troubleshooting
Common Issues
1. "Cannot find module"
Ensure all dependencies are installed:
npm install
cd modules/<module-name> && npm install
2. Validation failures
Check that your code compiles before running migrations. Leviathan assumes syntactically valid input.
3. Partial transformations
If some patterns aren't transformed, check the AST-grep rules match your code structure. Use --dry-run to preview.
4. Python module not found
Ensure Python 3.10+ is installed and the Ape Framework:
pip install ape-framework
Getting Help
- GitHub Issues: Report bugs and feature requests
- Discord: Join the community for support
- Documentation: Check module-specific docs in each package
Command Reference
Global Options
-v, --verbose Verbose output
--disable-analytics Disable telemetry
-h, --help Print help
codemod run
codemod run <PACKAGE> [OPTIONS]
Arguments:
<PACKAGE> Package name with optional version (e.g., @org/package@1.0.0)
Options:
-t, --target <TARGET_PATH> Target path to run the codemod on
--dry-run Preview without applying changes
--no-interactive Run without prompts
--allow-dirty Allow uncommitted changes
--param <KEY=VALUE> Pass parameters to the codemod
--registry <URL> Custom registry URL
--force Force re-download even if cached
codemod jssg run
codemod jssg run <JS_FILE> [OPTIONS]
Arguments:
<JS_FILE> Path to the JavaScript transformation file
Options:
-t, --target <TARGET_PATH> Target directory to process
--language <LANG> Language: javascript, typescript, tsx
--dry-run Preview without applying
--no-interactive Run without prompts
--allow-dirty Allow uncommitted changes
--semantic-workspace <PATH> Enable cross-file analysis
--max-threads <N> Max concurrent threads (default: CPU cores)
codemod workflow
codemod workflow run -w <WORKFLOW_FILE> [OPTIONS]
Options:
-w, --workflow <FILE> Path to workflow.yaml
--no-interactive Run all steps without prompts
--allow-dirty Allow uncommitted changes
--dry-run Preview entire workflow
Tip: Use --dry-run with --verbose to see exactly what will be changed before applying.
Environment Variables
Required for Ethers/BigInt Migrations
ETHEREUM_RPC_URL=
Ethereum JSON-RPC endpoint (e.g., https://eth-mainnet.alchemyapi.io)
ETHEREUM_CHAIN_ID=1 Chain ID for type inference (1=mainnet, 5=goerli, etc.)
Required for Alchemy Web3
REACT_APP_ALCHEMY_KEY=
Alchemy API key for Web3.js migrations
ALCHEMY_API_KEY= Alternative env var for Alchemy SDK
Optional Configuration
LOG_LEVEL=info Logging verbosity: debug, info, warn, error
CODEMOD_REGISTRY=https://registry.npmjs.org
NODE_ENV=production Enables stricter validation
Python (Brownie to Ape)
APE_ETHEREUM_RPC= Ethereum RPC URL for Ape test networks
APE_ETHEREUM_CHAIN_ID= Chain ID for network configuration
WEB3_INFURA_PROJECT_ID= Infura project ID (alternative to Alchemy)
Security: Never commit API keys to version control. Use .env.example files and .gitignore.
Safety & Limitations
What Leviathan CAN Transform
- CommonJS to ES6 import transformations
- BigNumber method chains (
.add(),.sub(), etc.) - Provider pattern updates (Web3.js v1 → v4)
- Constructor enforcement for contract instances
- Event listener name changes
- Python import restructuring (Brownie → Ape)
What Leviathan CANNOT Transform
- Logic changes: Custom business logic or algorithm modifications
- Type inference: Code with ambiguous types may be skipped (safety feature)
- Dynamic imports:
import(variable)patterns cannot be analyzed - Macro/compiled code: Minified or compiled output (babel, webpack bundles)
- API breaking changes: Some v5 → v6 breaking changes require manual review
Known Edge Cases
- Contract ABIs with nested structures may require manual verification
- Multi-file dependencies across packages need
--semantic-workspace - Dynamic provider creation (
new Web3(provider)) requires runtime verification
Zero False Positives: If Leviathan cannot verify the transformation is safe, it skips the change and logs a warning. Review logs carefully.
CI/CD Integration
Automate Leviathan migrations in your continuous integration pipeline.
GitHub Actions
name: Leviathan Migration
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
migrate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm install
- name: Run Leviathan Migration
run: npm run smoke-test
env:
ALCHEMY_API_KEY: ${{ secrets.ALCHEMY_API_KEY }}
- name: Validate JS
run: npm run validate:js
- name: Commit Changes
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "chore: Leviathan migration"
file_pattern: "*.js *.ts"
GitLab CI
leviathan-migration:
image: node:20
script:
- npm install
- npm run smoke-test
- npm run validate:js
only:
- main
artifacts:
paths:
- modified/
when: always
Jenkins Pipeline
pipeline {
agent any
environment {
ALCHEMY_API_KEY = credentials('alchemy-api-key')
}
stages {
stage('Install') {
steps {
sh 'npm install'
}
}
stage('Migrate') {
steps {
sh 'npm run smoke-test'
}
}
stage('Validate') {
steps {
sh 'npm run validate:js'
}
}
stage('Commit') {
steps {
sh 'git add -A && git commit -m "chore: Leviathan migration"'
}
}
}
}
Pre-commit Hooks
# .pre-commit-config.yaml
repos:
- repo: https://github.com/your-org/project-leviathan
rev: v1.0.0
hooks:
- id: leviathan-migration
args: ['--target', '.']
stages: [pre-commit]
language: system
Environment-Specific Migrations
# Run only Web3.js migration
npx codemod jssg run modules/web3js-v4-evolution/scripts/codemod.ts \
--language javascript --target ./src --no-interactive
# Run only Ethers migration
npx codemod jssg run modules/ethers-v6-bridge/scripts/codemod.ts \
--language javascript --target ./src --no-interactive
# Run with environment awareness
CODEMOD_TARGET=production npx codemod workflow run -w workflow.yaml \
--no-interactive --allow-dirty
Best Practice: Always run with --dry-run first in CI to preview changes before applying.