Skip to content

Commit 45136c3

Browse files
committed
introduce deploy binary for contract deployment
1 parent 2d7807e commit 45136c3

File tree

7 files changed

+147
-35
lines changed

7 files changed

+147
-35
lines changed

Cargo.lock

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ tokio-stream = "0.1.17"
9393
tokio-tungstenite = { version = "0.27.0", features = ["rustls-tls-webpki-roots", "url"] }
9494
tokio-util = "0.7.15"
9595
toml = "0.8"
96+
toml_edit = "0.22"
9697
tonic = "0.14.1"
9798
tonic-prost = "0.14.1"
9899
tonic-prost-build = "0.14.1"
@@ -101,7 +102,7 @@ tower-http = { version = "0.6.6", features = ["trace", "request-id", "util"] }
101102
tracing = "0.1"
102103
tracing-subscriber = { version = "0.3.18", features = ["env-filter", "json"] }
103104
turmoil = "0.6.4"
104-
url = "2.5.4"
105+
url = { version = "2.5.4", features = ["serde"] }
105106
zeroize = { version = "1.8", features = ["zeroize_derive"] }
106107

107108
espresso-types = { git = "https://github.com/EspressoSystems/espresso-network.git" }

justfile

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,33 @@ test-individually:
134134
echo "Testing $pkg"; \
135135
cargo nextest run --no-tests=pass -p $pkg || exit 1; \
136136
done
137+
138+
test-contract-deploy:
139+
#!/bin/bash
140+
set -exo pipefail
141+
142+
# Kill any existing anvil processes to avoid port conflicts
143+
pkill anvil || true
144+
sleep 1
145+
146+
# Start anvil in background
147+
anvil --port 8545 > anvil.log 2>&1 &
148+
ANVIL_PID=$!
149+
echo $ANVIL_PID > .anvil.pid
150+
151+
# Set up cleanup function
152+
cleanup() {
153+
if [ -n "$ANVIL_PID" ]; then
154+
kill $ANVIL_PID 2>/dev/null || true
155+
fi
156+
rm -f .anvil.pid anvil.log
157+
}
158+
159+
# Ensure cleanup happens on exit
160+
trap cleanup EXIT
161+
162+
# Wait for anvil to start
163+
sleep 1
164+
165+
# Run the deploy command
166+
RUST_LOG=info cargo run --bin deploy --config ./test-configs/keymanager.toml

test-configs/keymanager.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
[wallet]
22
mnemonic = "test test test test test test test test test test test junk"
3-
index = 0
3+
account_index = 0
4+
5+
[deployments]
6+
chain_url = "http://127.0.0.1:8545" # replace with the RPC endpoint of the target chain
7+
key_manager = "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512"

timeboost-contract/Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,20 @@ rust-version.workspace = true
88
[dependencies]
99
alloy = { workspace = true }
1010
anyhow = { workspace = true }
11+
clap = { workspace = true }
1112
rand = { workspace = true }
1213
tracing = { workspace = true }
1314
serde = { workspace = true }
15+
timeboost-utils = { path = "../timeboost-utils" }
1416
tokio = { workspace = true }
1517
toml = { workspace = true }
18+
toml_edit = { workspace = true }
19+
tracing-subscriber = { workspace = true }
20+
url = { workspace = true }
1621

1722
[build-dependencies]
1823
alloy = { workspace = true }
24+
25+
[[bin]]
26+
name = "deploy"
27+
path = "src/binaries/deploy.rs"
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
//! CLI for contract deployment
2+
//!
3+
//! # Local test
4+
//! Run `just test-contract-deploy`
5+
use alloy::{primitives::Address, providers::WalletProvider};
6+
use anyhow::{Context, Result};
7+
use clap::Parser;
8+
use serde::Deserialize;
9+
use std::{fs, path::PathBuf};
10+
use timeboost_contract::provider::build_provider;
11+
use timeboost_utils::types::logging;
12+
use toml_edit::{DocumentMut, value};
13+
use url::Url;
14+
15+
#[derive(Clone, Debug, Parser)]
16+
struct Args {
17+
/// Config file storing `KeyManagerConfig`
18+
#[clap(short, long, default_value = "./test-configs/keymanager.toml")]
19+
config: PathBuf,
20+
}
21+
22+
/// Config type for the key manager who has the permission to update the KeyManager contract
23+
/// See `test-configs/keymanager.toml` for an example
24+
#[derive(Debug, Deserialize)]
25+
struct KeyManagerConfig {
26+
wallet: LocalWalletConfig,
27+
deployments: Deployments,
28+
}
29+
30+
#[derive(Debug, Deserialize)]
31+
struct LocalWalletConfig {
32+
mnemonic: String,
33+
account_index: u32,
34+
}
35+
36+
#[derive(Debug, Deserialize)]
37+
#[allow(dead_code)]
38+
struct Deployments {
39+
/// RPC endpoint of the target chain
40+
chain_url: Url,
41+
/// The contract address of KeyManager.sol proxy
42+
key_manager: Option<Address>,
43+
}
44+
45+
#[tokio::main]
46+
async fn main() -> Result<()> {
47+
logging::init_logging();
48+
49+
let args = Args::parse();
50+
let config_path = args.config;
51+
52+
tracing::info!(
53+
"Starting contract deployment with config: {:?}",
54+
config_path
55+
);
56+
57+
// Read and parse config file
58+
let config_content = fs::read_to_string(&config_path)
59+
.with_context(|| format!("Failed to read config file: {config_path:?}"))?;
60+
let config: KeyManagerConfig = toml::from_str(&config_content)
61+
.with_context(|| format!("Failed to parse config file: {config_path:?}"))?;
62+
tracing::info!("Config loaded successfully");
63+
64+
// Build provider
65+
let provider = build_provider(
66+
config.wallet.mnemonic,
67+
config.wallet.account_index,
68+
config.deployments.chain_url,
69+
);
70+
71+
let manager = provider.default_signer_address();
72+
tracing::info!("Deploying with maanger address: {manager:#x}");
73+
74+
// Deploy the KeyManager contract
75+
let km_addr = timeboost_contract::deployer::deploy_key_manager_contract(&provider, manager)
76+
.await
77+
.context("Failed to deploy KeyManager contract")?;
78+
tracing::info!("KeyManager deployed successfully at: {km_addr:#x}");
79+
80+
// Update the config file with the deployed address
81+
let mut doc = config_content
82+
.parse::<DocumentMut>()
83+
.with_context(|| format!("Failed to parse TOML in config file: {config_path:?}"))?;
84+
85+
// Set the key_manager address in the deployments section
86+
doc["deployments"]["key_manager"] = value(format!("{km_addr:#x}"));
87+
88+
// Write back to file
89+
fs::write(&config_path, doc.to_string())
90+
.with_context(|| format!("Failed to write updated config file: {config_path:?}"))?;
91+
92+
tracing::info!("Config file updated with KeyManager address: {km_addr:#x}");
93+
94+
Ok(())
95+
}

timeboost-contract/src/lib.rs

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@
55
use alloy::{
66
primitives::Address,
77
providers::{ProviderBuilder, WalletProvider},
8-
transports::http::reqwest::Url,
98
};
109
use anyhow::Result;
11-
use serde::Deserialize;
1210

1311
// Include the generated contract bindings
1412
// The build script auto-detects contracts and generates bindings in src/bindings/
@@ -17,39 +15,9 @@ pub mod deployer;
1715
pub mod provider;
1816
mod sol_types;
1917

20-
use provider::{HttpProviderWithWallet, TestProviderWithWallet, build_provider};
18+
use provider::TestProviderWithWallet;
2119
pub use sol_types::*;
2220

23-
/// Config type for the key manager who has the permission to update the KeyManager contract
24-
/// See `test-configs/keymanager.toml` for an example
25-
#[derive(Deserialize)]
26-
struct KeyManagerConfig {
27-
wallet: LocalWalletConfig,
28-
}
29-
30-
#[derive(Deserialize)]
31-
struct LocalWalletConfig {
32-
mnemonic: String,
33-
index: u32,
34-
}
35-
36-
/// Connect to a real blockchain, deploy the KeyManager contract, set the
37-
/// `TIMEBOOST_KEY_MANAGER_MNEMONIC` as the manager.
38-
/// Returns the (wallet provider, KeyManager address).
39-
pub async fn init_chain(chain: Url) -> Result<(HttpProviderWithWallet, Address)> {
40-
let config =
41-
toml::from_str::<KeyManagerConfig>(include_str!("../../test-configs/keymanager.toml"))?;
42-
43-
let mnemonic = config.wallet.mnemonic;
44-
let account_idx = config.wallet.index;
45-
let provider = build_provider(mnemonic, account_idx, chain);
46-
47-
let km_addr =
48-
deployer::deploy_key_manager_contract(&provider, provider.default_signer_address()).await?;
49-
50-
Ok((provider, km_addr))
51-
}
52-
5321
/// Similar to [`init_chain()`] but spawn a local test chain and deploy contracts there instead.
5422
pub async fn init_test_chain() -> Result<(TestProviderWithWallet, Address)> {
5523
// this provider wraps both the test chain instance (exit on drop), and the wallet provider

0 commit comments

Comments
 (0)