Skip to content
Merged
Show file tree
Hide file tree
Changes from 65 commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
5a54109
Improve anvil deployment with dual-fork support
gpmayorga Oct 17, 2025
ee91fbc
Refactor AnvilManager to use structured account data and improve key …
gpmayorga Oct 20, 2025
8f4e55d
Merge remote-tracking branch 'origin/main' into anvil-improvements
gpmayorga Oct 20, 2025
01a9a81
Enhance deployment error handling and configuration updates
gpmayorga Oct 20, 2025
21927e9
Refactor deploy script to enhance deployment steps and validation
gpmayorga Oct 20, 2025
81c8985
Merge branch 'anvil-improvements' into multi-chain-tests
gpmayorga Oct 20, 2025
fd697ce
Merge remote-tracking branch 'origin/main' into multi-chain-tests
gpmayorga Oct 20, 2025
fc9b8e7
add cross-chain tests to deploy.py
gpmayorga Oct 20, 2025
afe55f1
latest testnets deployment
gpmayorga Oct 21, 2025
da2f589
Update deploy script and crosschain validation for contract naming co…
gpmayorga Oct 21, 2025
29bc16a
update cross-chain readme (AI)
gpmayorga Oct 21, 2025
937edaf
change deploy to deploy to all testnets
gpmayorga Oct 21, 2025
54f18ed
standardize deploymentInfo
gpmayorga Oct 22, 2025
3f28382
Refactor deployment steps in deploy.py and related files
gpmayorga Oct 22, 2025
c39b28e
fix: discover pool & share tokens on spoke
wischli Oct 27, 2025
0c7998c
fix: trigger refund escrow deploy in spoke script if missing
wischli Oct 27, 2025
8bc1501
rewire for arbitrum-sepolia Axelar adapter
gpmayorga Oct 30, 2025
c3c331c
run dry-run when PR changes for deploy testnsets
gpmayorga Oct 31, 2025
22709bc
Refactor deployment steps in deploy.py and related files
gpmayorga Oct 31, 2025
16aa5bf
redeploy and re-wire adapters
gpmayorga Oct 31, 2025
0d2070d
Enhance deployment validation and handling
gpmayorga Nov 6, 2025
87fc5a2
feat: v3.1 Spell Validator PoC (#16)
wischli Oct 27, 2025
b2eea1d
deploy fixes (#14)
lemunozm Oct 29, 2025
e33d353
add setEpochIds to use in the migration (#21)
lemunozm Oct 29, 2025
ce27a02
deployment changes to support the migration (#22)
lemunozm Oct 29, 2025
dbfbda4
fix import script when name is after ../ (#25)
lemunozm Nov 3, 2025
a331e32
Spell: Migration v3.1 MVP (without pre- and post-validation) (#15)
lemunozm Nov 4, 2025
acc05d4
fix: hook address mismatch (#30)
wischli Nov 10, 2025
70f58de
Migration: testnet support (#29)
lemunozm Nov 11, 2025
cce7e0a
Migration: rely steps for root in migration phase (#32)
lemunozm Nov 13, 2025
b7c5ac2
Fix: Gateway unpaid reentrancy issue (#38)
lemunozm Nov 17, 2025
508d0f3
Add check to registerAsset for readability (#33)
lemunozm Nov 17, 2025
9788686
erc6909 support (#40)
lemunozm Nov 18, 2025
607ada3
Migration: testnet anvil fork test (#37)
lemunozm Nov 18, 2025
9c9932a
Force pool adapters (#28)
lemunozm Nov 19, 2025
d4e69cd
Merge branch 'main' into multi-chain-tests
gpmayorga Nov 20, 2025
ea6ad38
fix: update shared testnet account in config loading
wischli Nov 20, 2025
8c20c8d
upload contract addresses
lemunozm Dec 16, 2025
1590311
verify script
lemunozm Dec 16, 2025
539637f
Merge remote-tracking branch 'internal/v3-1-migration-addresses' into…
wischli Dec 17, 2025
185e93e
feat: add isolated cross-chain adapter script
wischli Jan 14, 2026
92b2f8b
ref: deprecate previous cross-chain testnet scripts
wischli Jan 14, 2026
ec4b987
Merge remote-tracking branch 'internal/main' into multi-chain-tests
wischli Jan 14, 2026
3790190
add asset protection
lemunozm Jan 15, 2026
1e60124
add comment
lemunozm Jan 15, 2026
fb701ab
fix adapters from spoke side
lemunozm Jan 15, 2026
79846a4
Merge remote-tracking branch 'internal/main' into multi-chain-tests-v2
wischli Jan 19, 2026
c417cc1
modify how adapter info is gathered and the valuator
lemunozm Jan 19, 2026
14f2e61
Merge remote-tracking branch 'internal/main' into spell-asset-protection
lemunozm Jan 19, 2026
08a1749
ensure post-validation order
lemunozm Jan 19, 2026
5b9682a
fix import
lemunozm Jan 19, 2026
faeeb2f
add support for adapters that are not enabled in some chains
lemunozm Jan 19, 2026
72d5b13
use 2/3 for all adapters, never deploy wormhole
lemunozm Jan 19, 2026
ad65fc5
Merge branch 'spell-asset-protection' into multi-chain-tests-v3
wischli Jan 19, 2026
61596a3
wip: enable chainlink isolated tests
wischli Jan 20, 2026
5c329ea
feat: add cheaper & simpler isolated adapter test (co-existence)
wischli Feb 3, 2026
4fae484
Merge remote-tracking branch 'internal/main' into multi-chain-tests-v3
wischli Feb 5, 2026
1117b25
Script: verifies contract from factories automatically (#135)
lemunozm Feb 4, 2026
e35193a
Add v3.1 audit reports (#762)
hieronx Jan 20, 2026
a02b3bc
Add deployment verification report (#763)
hieronx Feb 3, 2026
b3bbbcb
script: minor improvements
wischli Feb 10, 2026
d6649e0
Merge remote-tracking branch 'origin/main' into multi-chain-tests-v3
wischli Feb 10, 2026
50ae900
fmt
wischli Feb 10, 2026
16f4f8f
ref: cleanup, rm v1 of isolated tests
wischli Feb 10, 2026
9beffab
chore: rm env changes (probably outdated at this point)
wischli Feb 10, 2026
664d8cb
add crosschain test orchestration (ported from protocol-internal PR #13)
gpmayorga Feb 13, 2026
ec329dd
fix: update AnvilEnv initialization to include network parameters
gpmayorga Feb 13, 2026
21a7d14
Merge branch 'main' into multi-chain-tests-v3
gpmayorga Feb 13, 2026
38f7d66
feat: enhance cross-chain test orchestration in deploy.py
gpmayorga Feb 13, 2026
ae4a375
chore: sync crosschain & readme
wischli Feb 16, 2026
ff504d2
docs: rm hardcoded addresses
wischli Feb 16, 2026
54898d5
refactor: create arbitrum-sepolia anvil env file before hand
gpmayorga Feb 17, 2026
ddca332
fix: resolve anvil dual-fork deploy failures
gpmayorga Feb 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 8 additions & 26 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,9 @@ on:
- ".github/workflows/deploy.yml"
workflow_dispatch:
inputs:
network:
description: "Select the testnet to deploy to"
required: true
type: choice
options:
- "sepolia"
- "arbitrum-sepolia"
- "base-sepolia"

version:
description: "Deployment version (optional, helps avoid create3 collisions)"
required: true
description: "Deployment version (e.g. v3.1.5) - if not provided, the job will generate a unique version number"
required: false
type: string
default: "TEST"

Expand Down Expand Up @@ -45,7 +36,8 @@ jobs:
uses: foundry-rs/foundry-toolchain@v1
with:
version: v1.4.1
#Make sure all tools are installed and with proper versions

# Make sure all tools are installed and with proper versions
- name: Setup deployer tools
env:
CI_MODE: true
Expand All @@ -58,28 +50,18 @@ jobs:
id: set-vars
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
echo "network=sepolia" >> $GITHUB_OUTPUT
echo "version=PR-${{ github.run_number }}-${{ github.sha }}" >> $GITHUB_OUTPUT
echo "dry=--dry-run" >> $GITHUB_OUTPUT
else
echo "network=${{ github.event.inputs.network }}" >> $GITHUB_OUTPUT
if [ "${{ github.event.inputs.version }}" = "TEST" ]; then
echo "version=TEST-${{ github.run_number }}-${{ github.sha }}" >> $GITHUB_OUTPUT
else
echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
fi
echo "dry=" >> $GITHUB_OUTPUT
fi
- name: Deploy protocol to ${{ steps.set-vars.outputs.network }}
env:
VERSION: ${{ steps.set-vars.outputs.version }}
run: |
python3 script/deploy/deploy.py ${{ steps.set-vars.outputs.network }} deploy:protocol
- name: Wire adapters to ${{ steps.set-vars.outputs.network }}
env:
VERSION: ${{ steps.set-vars.outputs.version }}
run: |
python3 script/deploy/deploy.py ${{ steps.set-vars.outputs.network }} wire:adapters
- name: Deploy Test data for ${{ steps.set-vars.outputs.network }}
- name: Deploying all testnets
env:
VERSION: ${{ steps.set-vars.outputs.version }}
run: |
python3 script/deploy/deploy.py ${{ steps.set-vars.outputs.network }} deploy:test
python3 script/deploy/deploy.py ${{ steps.set-vars.outputs.dry }} deploy:testnets
221 changes: 149 additions & 72 deletions script/deploy/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
import os
import traceback
import time
import json
from lib.formatter import *
from lib.load_config import EnvironmentLoader
from lib.runner import DeploymentRunner
from lib.verifier import ContractVerifier
from lib.anvil import AnvilManager
from lib.release import ReleaseManager
from lib.crosschain import CrossChainTestManager


def create_parser() -> argparse.ArgumentParser:
Expand All @@ -35,20 +37,24 @@ def create_parser() -> argparse.ArgumentParser:
- Run with VERSION=XYZ preceding the python3 command to avoid create3 collisions.

Examples:
VERSION=vXYZ python3 deploy.py sepolia deploy:protocol
VERSION=vXYZ python3 deploy.py sepolia deploy:full
python3 deploy.py base-sepolia deploy:full --catapulta --priority-gas-price 2
python3 deploy.py sepolia deploy:test --resume
python3 deploy.py sepolia deploy:adapters
python3 deploy.py sepolia deploy:adapters --resume
python3 deploy.py sepolia verify:protocol
python3 deploy.py sepolia verify:contracts # Verify & merge all contracts from latest deployment
python3 deploy.py arbitrum-sepolia verify:protocol
VERSION=vXYZ python3 deploy.py release:sepolia # Deploy all Sepolia testnets (auto-resumes)
VERSION=vXYZ python3 deploy.py deploy:testnets # Deploy all Sepolia testnets (auto-resumes)
python3 deploy.py sepolia crosschaintest:hub # Run cross-chain hub test
python3 deploy.py base-sepolia crosschaintest:spoke # Run cross-chain spoke tests
"""
)

parser.add_argument("network", nargs="?", help="Network name (must match env/<network>.json)")
parser.add_argument("step", nargs="?", help="Deployment step", choices=[
"deploy:protocol", "deploy:full", "deploy:adapters", "wire:adapters",
"deploy:test", "verify:protocol", "verify:contracts", "dump:config", "release:sepolia"
"deploy:test", "verify:protocol", "verify:contracts", "dump:config", "release:sepolia",
"crosschaintest:hub", "crosschaintest:spoke"
])
parser.add_argument("--catapulta", action="store_true", help="Use Catapulta for deployment")
parser.add_argument("--ledger", action="store_true", help="Force use of Ledger hardware wallet")
Expand Down Expand Up @@ -127,23 +133,24 @@ def main():
# Add unknown arguments as forge_args
args.forge_args = unknown_args

# Normalize only the special case where the single positional is the testnets step
# Example: python3 deploy.py deploy:testnets [--flags]
if args.step is None and args.network == "deploy:testnets":
args.step = "deploy:testnets"
args.network = None

# Get root directory early for validation
script_dir = pathlib.Path(__file__).parent
root_dir = script_dir.parent.parent

# Handle special case where release:sepolia is passed as network argument
if args.network == "release:sepolia":
args.step = "release:sepolia"
args.network = None

# Validate arguments
if args.network != "anvil" and args.step != "release:sepolia":
if args.network != "anvil" and args.step != "deploy:testnets":
validate_arguments(args, root_dir)
elif args.step == "release:sepolia":
# Special validation for release:sepolia
if not os.environ.get("VERSION"):
print_error("VERSION environment variable is required for release:sepolia")
print_info("Example: VERSION=v3.1.4 python3 script/deploy/deploy.py release:sepolia")
elif args.step == "deploy:testnets":
# Special validation for deploy:testnets
if not os.environ.get("VERSION") and not args.dry_run:
print_error("VERSION environment variable is required for deploy:testnets")
print_info("Example: VERSION=v3.1.4 python3 script/deploy/deploy.py deploy:testnets")
sys.exit(1)

try:
Expand All @@ -153,7 +160,7 @@ def main():
success = anvil_manager.deploy_full_protocol()
sys.exit(0 if success else 1)

if args.step != "release:sepolia":
if args.step != "deploy:testnets":
# Create environment loader for single network deployments
env_loader = EnvironmentLoader(
network_name=args.network,
Expand All @@ -166,11 +173,11 @@ def main():
print_info(f"Deployment mode: {'Catapulta' if args.catapulta else 'Forge'}")

# Validate network configuration for deployment and wiring steps
if args.step in ["deploy:protocol", "deploy:full", "deploy:adapters", "wire:adapters", "deploy:test"]:
if args.step in ["deploy:full", "deploy:adapters", "wire"]:
env_loader.validate_network()

# Set up deployment runner and verifier (only for deployment steps)
if args.step != "dump:config":
if args.step != "config:dump":
runner = DeploymentRunner(env_loader, args)
verifier = ContractVerifier(env_loader, args)

Expand All @@ -179,68 +186,73 @@ def main():
deploy_success = True

if args.step == "deploy:full":
print_section("Running Full Deployment")
runner.build_contracts()
print_subsection(f"Deploying core protocol contracts for {args.network}")
retries = 3
# Deploy protocol Core contracts
while not runner.run_deploy("LaunchDeployer"):
retries -= 1
# Add --resume to continue from where we left off after first try
if "--resume" not in args.forge_args:
args.forge_args.append("--resume")
if retries ==0:
print_error("Full deployment failed")
sys.exit(1)
else:
print_error("Full deployment failed, retrying {retries}/3")
time.sleep(10)
print_section(f"Verifying deployment for {args.network}")
if not verifier.verify_contracts("LaunchDeployer"):
print_error("Full deployment verification failed. Check logs for details.")
sys.exit(1)
print_success("Full deployment completed successfully")

# Deploy Test Data on testnets
if env_loader.is_testnet:
print_info("Running test data deployment")
if not runner.run_deploy("TestData"):
print_error("Test data deployment failed")
sys.exit(1)
print_success("Test data deployment completed successfully")
sys.exit(0)

elif args.step == "deploy:protocol":
print_section("Running Protocol Deployment")

if "--resume" not in args.forge_args:
runner.build_contracts()

print_subsection(f"Deploying core protocol contracts for {args.network}")
deploy_success = runner.run_deploy("LaunchDeployer")
print_section(f"Verifying deployment for {args.network}")
if args.catapulta:
print_info("Waiting for catapulta verification to complete...")
# Retry verification up to 3 times for catapulta since verification happens on their servers
retries = 3
verify_success = False
while not verify_success and retries > 0:
print_info(f"Verification attempt {4-retries}/3 for catapulta...")
time.sleep(120) # Wait 2 minutes between attempts
already_deployed = False
if "--resume" in args.forge_args and not args.dry_run:
already_deployed = verifier.config_has_latest_contracts()

# Why did we need to build before running forge script?
# if "--resume" not in args.forge_args and not already_deployed:
# runner.build_contracts()

if already_deployed:
print_info("Protocol contracts deployed and verified. Running TestData...")
deploy_success = True
else:
print_subsection(f"Deploying core protocol contracts for {args.network}")
deploy_success = runner.run_deploy("LaunchDeployer")

# Skip verification in dry-run mode
if not args.dry_run:
print_section(f"Verifying deployment for {args.network}")
if args.catapulta and not already_deployed:
print_info("Waiting for catapulta verification to complete...")
# Retry verification up to 3 times for catapulta since verification happens on their servers
retries = 3
verify_success = False
while not verify_success and retries > 0:
print_info(f"Verification attempt {4-retries}/3 for catapulta...")
time.sleep(120) # Wait 2 minutes between attempts
verify_success = verifier.verify_contracts("LaunchDeployer")
if not verify_success and retries > 1:
print_warning("Verification failed, retrying...")
retries -= 1
if not verify_success:
print_error("Verification failed after 3 attempts")
sys.exit(1)
elif not already_deployed:
# Forge would only get there if the --verify has completed
verify_success = verifier.verify_contracts("LaunchDeployer")
if not verify_success and retries > 1:
print_warning("Verification failed, retrying...")
retries -= 1
print_error("Verification failed after 3 attempts")
else:
# Forge would only get there if the --verify has completed
verify_success = verifier.verify_contracts("LaunchDeployer")
print_info("Dry-run mode: skipping verification")
verify_success = True

# Auto-run TestData on testnets (skip in dry-run)
if verify_success and env_loader.is_testnet and not args.dry_run:
print_info("Auto-running TestData for testnet")
if "--resume" in args.forge_args and not already_deployed:
# User triggered command with --resume, probably because the protocol deployment failed
# but it is the first time we're running TestData, so we need to remove --resume this time
original_forge_args = list(args.forge_args)
args.forge_args = [a for a in args.forge_args if a != "--resume"]
if not runner.run_deploy("TestData"):
print_error("TestData deployment failed")
sys.exit(1)
print_success("TestData deployment completed successfully")
# Restore forge args
args.forge_args = original_forge_args
elif args.dry_run:
print_info("Dry-run mode: skipping TestData deployment")

elif args.step == "verify":
print_section(f"Verifying core protocol contracts for {args.network}")
verify_success = verifier.verify_contracts("LaunchDeployer")

elif args.step == "deploy:adapters":
print_section(f"Deploying adapters only for {args.network}")
deploy_success = runner.run_deploy("OnlyAdapters")
# After deploying with forge, also run our verifier to merge env/latest into env/<network>.json
if deploy_success:
if deploy_success and not args.dry_run:
print_section(f"Verifying deployment for {args.network}")
verify_success = verifier.verify_contracts("OnlyAdapters")

Expand Down Expand Up @@ -271,6 +283,71 @@ def main():
success = release_manager.deploy_sepolia_testnets()
sys.exit(0 if success else 1)

elif args.step == "wire":
print_step(f"Wiring adapters for {args.network}")
deploy_success = runner.run_deploy("WireAdapters")

elif args.step == "wire:all":
print_section("Wiring adapters across connected networks")
# Load current network config
connects = []
try:
with open(env_loader.config_file, 'r') as f:
cfg = json.load(f)
connects = cfg.get('network', {}).get('connectsTo', []) or []
except Exception as e:
print_error(f"Failed to read network config: {e}")
sys.exit(1)

all_networks = [args.network] + connects
unique_networks = []
for n in all_networks:
if n and n not in unique_networks:
unique_networks.append(n)

print_warning(f"About to wire adapters for {len(unique_networks)} networks: {', '.join(unique_networks)}")
print_warning("Ensure each network has the latest verified deployment. Press Ctrl+C to abort.")
try:
time.sleep(10)
except KeyboardInterrupt:
print_info("Aborted by user before wiring started.")
sys.exit(1)

# Run wiring for current network first
print_step(f"Wiring adapters for {args.network}")
if not runner.run_deploy("WireAdapters"):
sys.exit(1)

# Then wire for each connected network by swapping env loader
for network_name in connects:
print_section(f"Switching to {network_name} for wiring")
# Recreate EnvironmentLoader, Runner, Verifier for the target network
target_env_loader = EnvironmentLoader(network_name, root_dir, args)
target_runner = DeploymentRunner(target_env_loader, args)
print_step(f"Wiring adapters for {network_name}")
if not target_runner.run_deploy("WireAdapters"):
print_error(f"Wiring failed for {network_name}")
sys.exit(1)
deploy_success = True

elif args.step == "config:dump":
print_section(f"Dumping config for {args.network}")
env_loader.dump_config()

elif args.step == "crosschaintest:hub":
print_section("Cross-Chain Hub Test")
crosschain_manager = CrossChainTestManager(env_loader, args, root_dir)
result = crosschain_manager.run_hub_test()
print_success("Cross-chain hub test completed successfully")
sys.exit(0)

elif args.step == "crosschaintest:spoke":
print_section("Cross-Chain Spoke Tests")
crosschain_manager = CrossChainTestManager(env_loader, args, root_dir)
result = crosschain_manager.run_spoke_tests()
print_success("Cross-chain spoke tests completed")
sys.exit(0)

# Handle errors
if not verify_success:
if args.catapulta:
Expand Down
Loading
Loading