diff --git a/src/contract_clients/config.rs b/src/contract_clients/config.rs index 2685404..fc57cd6 100644 --- a/src/contract_clients/config.rs +++ b/src/contract_clients/config.rs @@ -1,3 +1,4 @@ +use anyhow::Context; use ethereum_instance::EthereumClient; use starknet_providers::jsonrpc::HttpTransport; use starknet_providers::JsonRpcClient; @@ -20,18 +21,18 @@ impl Config { } /// To deploy the instance of ethereum and starknet and returning the struct. - pub async fn init(config: &CliArgs) -> Self { + pub async fn init(config: &CliArgs) -> anyhow::Result { let client_instance = EthereumClient::attach( - Option::from(config.eth_rpc.clone()), - Option::from(config.eth_priv_key.clone()), - Option::from(config.eth_chain_id), + Some(config.eth_rpc.clone()), + Some(config.eth_priv_key.clone()), + Some(config.eth_chain_id), ) - .unwrap(); + .context("Creating the Ethereum RPC client")?; let provider_l2 = JsonRpcClient::new(HttpTransport::new( Url::parse(&config.rollup_seq_url).expect("Failed to declare provider for app chain"), )); - Self { eth_client: client_instance, provider_l2 } + Ok(Self { eth_client: client_instance, provider_l2 }) } } diff --git a/src/contract_clients/core_contract.rs b/src/contract_clients/core_contract.rs index 8468a36..42bf593 100644 --- a/src/contract_clients/core_contract.rs +++ b/src/contract_clients/core_contract.rs @@ -21,7 +21,7 @@ pub trait CoreContract { fn client(&self) -> Arc; - async fn initialize_with(&self, init_data: CoreContractInitData); + async fn initialize_with(&self, init_data: CoreContractInitData) -> anyhow::Result<()>; #[allow(clippy::too_many_arguments)] async fn add_implementation_core_contract( @@ -33,7 +33,7 @@ pub trait CoreContract { implementation_address: Address, verifier_address: Address, finalized: bool, - ); + ) -> anyhow::Result<()>; #[allow(clippy::too_many_arguments)] async fn upgrade_to_core_contract( @@ -45,15 +45,15 @@ pub trait CoreContract { implementation_address: Address, verifier_address: Address, finalized: bool, - ); + ) -> anyhow::Result<()>; - async fn register_operator_core_contract(&self, operator_address: Address); + async fn register_operator_core_contract(&self, operator_address: Address) -> anyhow::Result<()>; - async fn nominate_governor_core_contract(&self, l1_governor_address: Address); + async fn nominate_governor_core_contract(&self, l1_governor_address: Address) -> anyhow::Result<()>; - async fn nominate_governor_core_contract_proxy(&self, l1_governor_address: Address); + async fn nominate_governor_core_contract_proxy(&self, l1_governor_address: Address) -> anyhow::Result<()>; - async fn initialize(&self, program_hash: StarkFelt, config_hash: StarkFelt); + async fn initialize(&self, program_hash: StarkFelt, config_hash: StarkFelt) -> anyhow::Result<()>; async fn initialize_core_contract( &self, @@ -62,11 +62,11 @@ pub trait CoreContract { program_hash: FieldElement, config_hash: StarkHash, verifer_address: Address, - ); + ) -> anyhow::Result<()>; } pub trait CoreContractDeploy { - fn deploy(config: &Config) -> impl Future + Send; + fn deploy(config: &Config) -> impl Future> + Send; } pub fn get_init_data_core_contract( diff --git a/src/contract_clients/eth_bridge.rs b/src/contract_clients/eth_bridge.rs index f6df7ba..319a5f4 100644 --- a/src/contract_clients/eth_bridge.rs +++ b/src/contract_clients/eth_bridge.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use anyhow::Context; use async_trait::async_trait; use ethers::addressbook::Address; use ethers::providers::Middleware; @@ -21,8 +22,8 @@ use crate::helpers::account_actions::{get_contract_address_from_deploy_tx, Accou use crate::utils::{invoke_contract, wait_for_transaction}; #[async_trait] -pub trait BridgeDeployable { - async fn deploy(client: Arc, is_dev: bool) -> Self; +pub trait BridgeDeployable: Sized { + async fn deploy(client: Arc, is_dev: bool) -> anyhow::Result; } pub struct StarknetLegacyEthBridge { @@ -31,17 +32,17 @@ pub struct StarknetLegacyEthBridge { #[async_trait] impl BridgeDeployable for StarknetLegacyEthBridge { - async fn deploy(client: Arc, is_dev: bool) -> Self { + async fn deploy(client: Arc, is_dev: bool) -> anyhow::Result { let eth_bridge = match is_dev { true => deploy_starknet_eth_bridge_behind_unsafe_proxy(client.clone()) .await - .expect("Failed to deploy starknet contract"), + .context("Failed to deploy starknet contract")?, false => deploy_starknet_eth_bridge_behind_safe_proxy(client.clone()) .await - .expect("Failed to deploy starknet contract"), + .context("Failed to deploy starknet contract")?, }; - Self { eth_bridge } + Ok(Self { eth_bridge }) } } @@ -63,7 +64,7 @@ impl StarknetLegacyEthBridge { legacy_eth_bridge_class_hash: FieldElement, legacy_eth_bridge_proxy_address: FieldElement, account: &RpcAccount<'_>, - ) -> FieldElement { + ) -> anyhow::Result { let deploy_tx = account .invoke_contract( account.address(), @@ -71,17 +72,20 @@ impl StarknetLegacyEthBridge { vec![legacy_eth_bridge_class_hash, FieldElement::ZERO, FieldElement::ZERO, FieldElement::ZERO], None, ) + .context("Creating the invoke transaction for contract proxy deployment")? .send() .await - .expect("Error deploying the contract proxy."); + .context("Error deploying the contract proxy")?; wait_for_transaction( rpc_provider_l2, deploy_tx.transaction_hash, "deploy_l2_contracts : deploy_contract : eth bridge", ) .await - .unwrap(); - let contract_address = get_contract_address_from_deploy_tx(account.provider(), &deploy_tx).await.unwrap(); + .context("Waiting for contract proxy deployment")?; + let contract_address = get_contract_address_from_deploy_tx(account.provider(), &deploy_tx) + .await + .context("Getting contract address from deploy transaction")?; log::debug!("🎡 contract address (eth bridge) : {:?}", contract_address); @@ -91,7 +95,8 @@ impl StarknetLegacyEthBridge { vec![contract_address, FieldElement::ZERO, FieldElement::ONE, account.address(), FieldElement::ZERO], account, ) - .await; + .await + .context("Creating the add_implementation transaction")?; wait_for_transaction( rpc_provider_l2, @@ -99,7 +104,7 @@ impl StarknetLegacyEthBridge { "deploy_l2_contracts : add_implementation : eth bridge", ) .await - .unwrap(); + .context("Waiting for the add_implementation transaction to settle")?; let upgrade_to_txn = invoke_contract( legacy_eth_bridge_proxy_address, @@ -107,7 +112,8 @@ impl StarknetLegacyEthBridge { vec![contract_address, FieldElement::ZERO, FieldElement::ONE, account.address(), FieldElement::ZERO], account, ) - .await; + .await + .context("Creating the upgrade_to transaction")?; wait_for_transaction( rpc_provider_l2, @@ -115,14 +121,14 @@ impl StarknetLegacyEthBridge { "deploy_l2_contracts : upgrade_to : eth bridge", ) .await - .unwrap(); + .context("Waiting for the upgrade_to transaction to settle")?; - legacy_eth_bridge_proxy_address + Ok(legacy_eth_bridge_proxy_address) } /// Initialize Starknet Legacy Eth Bridge /// IMP : only need to be called when using unsafe proxy - pub async fn initialize(&self, messaging_contract: Address) { + pub async fn initialize(&self, messaging_contract: Address) -> anyhow::Result<()> { let empty_bytes = [0u8; 32]; let messaging_bytes = messaging_contract.as_bytes(); @@ -136,11 +142,12 @@ impl StarknetLegacyEthBridge { calldata.extend(empty_bytes); calldata.extend(padded_messaging_bytes); - self.eth_bridge.initialize(Bytes::from(calldata)).await.expect("Failed to initialize eth bridge"); + self.eth_bridge.initialize(Bytes::from(calldata)).await.context("Failed to initialize eth bridge")?; + Ok(()) } /// Add Implementation Starknet Legacy Eth Bridge - pub async fn add_implementation_eth_bridge(&self, messaging_contract: Address) { + pub async fn add_implementation_eth_bridge(&self, messaging_contract: Address) -> anyhow::Result<()> { let empty_bytes = [0u8; 32]; let messaging_bytes = messaging_contract.as_bytes(); @@ -163,11 +170,12 @@ impl StarknetLegacyEthBridge { self.eth_bridge .add_implementation(Bytes::from(calldata), self.implementation_address(), false) .await - .expect("Failed to initialize eth bridge"); + .context("Failed to initialize eth bridge")?; + Ok(()) } /// Upgrade To Starknet Legacy Eth Bridge - pub async fn upgrade_to_eth_bridge(&self, messaging_contract: Address) { + pub async fn upgrade_to_eth_bridge(&self, messaging_contract: Address) -> anyhow::Result<()> { let empty_bytes = [0u8; 32]; let messaging_bytes = messaging_contract.as_bytes(); @@ -190,7 +198,9 @@ impl StarknetLegacyEthBridge { self.eth_bridge .upgrade_to(Bytes::from(calldata), self.implementation_address(), false) .await - .expect("Failed to initialize eth bridge"); + .context("Failed to upgrade the eth bridge")?; + + Ok(()) } /// Sets up the Eth bridge with the specified data @@ -201,15 +211,30 @@ impl StarknetLegacyEthBridge { l2_bridge: FieldElement, l1_multisig_address: Address, is_dev: bool, - ) { - self.eth_bridge.set_max_total_balance(U256::from_dec_str(max_total_balance).unwrap()).await.unwrap(); - self.eth_bridge.set_max_deposit(U256::from_dec_str(max_deposit).unwrap()).await.unwrap(); - self.eth_bridge.set_l2_token_bridge(field_element_to_u256(l2_bridge)).await.unwrap(); + ) -> anyhow::Result<()> { + self.eth_bridge + .set_max_total_balance( + U256::from_dec_str(max_total_balance).context("Converting max total balance to U256")?, + ) + .await + .context("Setting max total balance")?; + self.eth_bridge + .set_max_deposit(U256::from_dec_str(max_deposit).context("Converting max deposit to U256")?) + .await + .context("Setting the max deposit")?; + self.eth_bridge + .set_l2_token_bridge(field_element_to_u256(l2_bridge)) + .await + .context("Setting the L2 token bridge")?; if !is_dev { // Nominating a new governor as l1 multi sig address - self.eth_bridge.proxy_nominate_new_governor(l1_multisig_address).await.unwrap(); + self.eth_bridge + .proxy_nominate_new_governor(l1_multisig_address) + .await + .context("Nominating the proxy governor")?; } + Ok(()) } pub async fn setup_l2_bridge( @@ -219,61 +244,84 @@ impl StarknetLegacyEthBridge { erc20_address: FieldElement, l2_deployer_address: &str, account: &RpcAccount<'_>, - ) { + ) -> anyhow::Result<()> { let tx = invoke_contract( l2_bridge_address, "initialize", - vec![FieldElement::from_dec_str("1").unwrap(), FieldElement::from_hex_be(l2_deployer_address).unwrap()], + vec![ + FieldElement::from_dec_str("1").expect("Converting a constant"), + FieldElement::from_hex_be(l2_deployer_address).context("Parsing the L2 deployer address")?, + ], account, ) - .await; + .await + .context("Creating the initialize transaction")?; log::debug!("🎡 setup_l2_bridge : l2 bridge initialized //"); - wait_for_transaction(rpc_provider, tx.transaction_hash, "setup_l2_bridge : initialize").await.unwrap(); + wait_for_transaction(rpc_provider, tx.transaction_hash, "setup_l2_bridge : initialize") + .await + .context("Waiting for the initialize transaction to settle")?; - let tx = invoke_contract(l2_bridge_address, "set_l2_token", vec![erc20_address], account).await; + let tx = invoke_contract(l2_bridge_address, "set_l2_token", vec![erc20_address], account) + .await + .context("Creating the set_l2_token transaction")?; log::debug!("🎡 setup_l2_bridge : l2 token set //"); - wait_for_transaction(rpc_provider, tx.transaction_hash, "setup_l2_bridge : set_l2_token").await.unwrap(); + wait_for_transaction(rpc_provider, tx.transaction_hash, "setup_l2_bridge : set_l2_token") + .await + .context("Waiting for the set_l2_token transaction to settle")?; let tx = invoke_contract( l2_bridge_address, "set_l1_bridge", - vec![FieldElement::from_byte_slice_be(self.eth_bridge.address().as_bytes()).unwrap()], + vec![ + FieldElement::from_byte_slice_be(self.eth_bridge.address().as_bytes()) + .context("Parsing the eth_bridge address")?, + ], account, ) - .await; + .await + .context("Creating the set_l1_bridge transaction")?; log::debug!("🎡 setup_l2_bridge : l1 bridge set //"); - wait_for_transaction(rpc_provider, tx.transaction_hash, "setup_l2_bridge : set_l1_bridge").await.unwrap(); + wait_for_transaction(rpc_provider, tx.transaction_hash, "setup_l2_bridge : set_l1_bridge") + .await + .context("Waiting for the set_l1_bridge transaction to settle")?; + + Ok(()) } - pub async fn set_max_total_balance(&self, amount: U256) { - self.eth_bridge - .set_max_total_balance(amount) - .await - .expect("Failed to set max total balance value in Eth bridge"); + pub async fn set_max_total_balance(&self, amount: U256) -> anyhow::Result<()> { + self.eth_bridge.set_max_total_balance(amount).await.context("Setting the max total balance")?; + Ok(()) } - pub async fn set_max_deposit(&self, amount: U256) { - self.eth_bridge.set_max_deposit(amount).await.expect("Failed to set max deposit value in eth bridge"); + pub async fn set_max_deposit(&self, amount: U256) -> anyhow::Result<()> { + self.eth_bridge.set_max_deposit(amount).await.context("Failed to set max deposit value in eth bridge")?; + Ok(()) } - pub async fn set_l2_token_bridge(&self, l2_bridge: U256) { - self.eth_bridge.set_l2_token_bridge(l2_bridge).await.expect("Failed to set l2 bridge in eth bridge"); + pub async fn set_l2_token_bridge(&self, l2_bridge: U256) -> anyhow::Result<()> { + self.eth_bridge.set_l2_token_bridge(l2_bridge).await.context("Failed to set l2 bridge in eth bridge")?; + Ok(()) } - pub async fn deposit(&self, amount: U256, l2_address: U256, fee: U256) { - self.eth_bridge.deposit(amount, l2_address, fee).await.expect("Failed to deposit in eth bridge"); + pub async fn deposit(&self, amount: U256, l2_address: U256, fee: U256) -> anyhow::Result<()> { + self.eth_bridge.deposit(amount, l2_address, fee).await.context("Failed to deposit in eth bridge")?; + Ok(()) } - pub async fn withdraw(&self, amount: U256, l1_recipient: Address) { - self.eth_bridge.withdraw(amount, l1_recipient).await.expect("Failed to withdraw from eth bridge"); + pub async fn withdraw(&self, amount: U256, l1_recipient: Address) -> anyhow::Result<()> { + self.eth_bridge.withdraw(amount, l1_recipient).await.context("Failed to withdraw from eth bridge")?; + Ok(()) } - pub async fn eth_balance(&self, l1_recipient: Address) -> U256 { + pub async fn eth_balance(&self, l1_recipient: Address) -> anyhow::Result { let provider = self.eth_bridge.client().provider().clone(); - provider.get_balance(l1_recipient, None).await.unwrap() + provider + .get_balance(l1_recipient, None) + .await + .with_context(|| format!("Getting the eth balance for {}", l1_recipient)) } } diff --git a/src/contract_clients/starknet_sovereign.rs b/src/contract_clients/starknet_sovereign.rs index 247f567..142ce1d 100644 --- a/src/contract_clients/starknet_sovereign.rs +++ b/src/contract_clients/starknet_sovereign.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use anyhow::Context; use async_trait::async_trait; use ethers::types::Address; use starknet_api::hash::{StarkFelt, StarkHash}; @@ -21,12 +22,12 @@ pub struct StarknetSovereignContract { } impl CoreContractDeploy for StarknetSovereignContract { - async fn deploy(config: &Config) -> Self { + async fn deploy(config: &Config) -> anyhow::Result { let client = deploy_starknet_sovereign_behind_unsafe_proxy(config.eth_client().signer().clone()) .await - .expect("Failed to deploy the starknet contact"); + .context("Failed to deploy the starknet contact")?; - Self { core_contract_client: client } + Ok(Self { core_contract_client: client }) } } @@ -50,17 +51,18 @@ impl CoreContract for StarknetSovereignContract { /// Initialize Starknet core contract with the specified data. /// IMP : only need to be called when using unsafe proxy - async fn initialize_with(&self, init_data: CoreContractInitData) { + async fn initialize_with(&self, init_data: CoreContractInitData) -> anyhow::Result<()> { let data = ProxyInitializeData::<0> { sub_contract_addresses: [], eic_address: Default::default(), init_data }; log::debug!("ℹī¸ initialize_with : data : {:?}", data); - self.core_contract_client.initialize_with(data).await.expect("Failed to initialize"); + self.core_contract_client.initialize_with(data).await.context("Failed to initialize")?; self.core_contract_client .register_operator(self.core_contract_client.client().address()) .await - .expect("Failed to register operator"); + .context("Failed to register operator")?; + Ok(()) } /// Add implementation Starknet core contract with the specified data. @@ -74,7 +76,7 @@ impl CoreContract for StarknetSovereignContract { implementation_address: Address, verifier_address: Address, finalized: bool, - ) { + ) -> anyhow::Result<()> { let program_hash = StarkFelt(program_hash.to_bytes_be()); let init_data = @@ -95,6 +97,7 @@ impl CoreContract for StarknetSovereignContract { .expect("Failed to call add implementation"); log::debug!("ℹī¸ add_implementation : done"); + Ok(()) } /// Add implementation Starknet core contract with the specified data. @@ -108,7 +111,7 @@ impl CoreContract for StarknetSovereignContract { implementation_address: Address, verifier_address: Address, finalized: bool, - ) { + ) -> anyhow::Result<()> { let program_hash = StarkFelt(program_hash.to_bytes_be()); let init_data = @@ -129,42 +132,46 @@ impl CoreContract for StarknetSovereignContract { .expect("Failed to call upgrade to"); log::debug!("ℹī¸ upgrade_to : done"); + Ok(()) } /// For registering the operator for Starknet Core Contract - async fn register_operator_core_contract(&self, operator_address: Address) { - self.core_contract_client.register_operator(operator_address).await.expect("Failed to register operator"); + async fn register_operator_core_contract(&self, operator_address: Address) -> anyhow::Result<()> { + self.core_contract_client.register_operator(operator_address).await.context("Failed to register operator")?; log::debug!("ℹī¸ register_operator : done"); + Ok(()) } /// For nominating the governor for Starknet Core Contract - async fn nominate_governor_core_contract(&self, l1_governor_address: Address) { + async fn nominate_governor_core_contract(&self, l1_governor_address: Address) -> anyhow::Result<()> { self.core_contract_client .starknet_nominate_new_governor(l1_governor_address) .await - .expect("Failed to nominate governor"); + .context("Failed to nominate governor")?; log::debug!("ℹī¸ register_operator : done"); + Ok(()) } /// For nominating the governor for Starknet Core Contract Proxy - async fn nominate_governor_core_contract_proxy(&self, l1_governor_address: Address) { + async fn nominate_governor_core_contract_proxy(&self, l1_governor_address: Address) -> anyhow::Result<()> { self.core_contract_client .proxy_nominate_new_governor(l1_governor_address) .await - .expect("Failed to register operator"); + .context("Failed to register operator")?; log::debug!("ℹī¸ proxy_nominate_new_governor : done"); + Ok(()) } /// Initialize Starknet core contract with the specified program and config hashes. The rest of /// parameters will be left default. /// IMP : only need to be called when using unsafe proxy - async fn initialize(&self, program_hash: StarkFelt, config_hash: StarkFelt) { + async fn initialize(&self, program_hash: StarkFelt, config_hash: StarkFelt) -> anyhow::Result<()> { self.initialize_with(CoreContractInitData { program_hash: convert_felt_to_u256(program_hash), config_hash: convert_felt_to_u256(config_hash), ..Default::default() }) - .await; + .await } /// Initialize Starknet core contract with the specified block number and state root hash. @@ -176,12 +183,12 @@ impl CoreContract for StarknetSovereignContract { program_hash: FieldElement, config_hash: StarkHash, verifer_address: Address, - ) { + ) -> anyhow::Result<()> { let program_hash = StarkFelt(program_hash.to_bytes_be()); let init_data = get_init_data_core_contract(block_number, state_root, program_hash, config_hash, verifer_address); - self.initialize_with(init_data).await; + self.initialize_with(init_data).await } } diff --git a/src/contract_clients/starknet_validity.rs b/src/contract_clients/starknet_validity.rs index 270baa1..e118939 100644 --- a/src/contract_clients/starknet_validity.rs +++ b/src/contract_clients/starknet_validity.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use anyhow::Context; use async_trait::async_trait; use ethers::types::Address; use starknet_api::hash::{StarkFelt, StarkHash}; @@ -21,12 +22,12 @@ pub struct StarknetValidityContract { } impl CoreContractDeploy for StarknetValidityContract { - async fn deploy(config: &Config) -> Self { + async fn deploy(config: &Config) -> anyhow::Result { let client = deploy_starknet_validity_behind_safe_proxy(config.eth_client().signer().clone()) .await - .expect("Failed to deploy the starknet contact"); + .context("Failed to deploy the starknet contact")?; - Self { core_contract_client: client } + Ok(Self { core_contract_client: client }) } } @@ -50,17 +51,18 @@ impl CoreContract for StarknetValidityContract { /// Initialize Starknet core contract with the specified data. /// IMP : only need to be called when using unsafe proxy - async fn initialize_with(&self, init_data: CoreContractInitData) { + async fn initialize_with(&self, init_data: CoreContractInitData) -> anyhow::Result<()> { let data = ProxyInitializeData::<0> { sub_contract_addresses: [], eic_address: Default::default(), init_data }; log::debug!("ℹī¸ initialize_with : data : {:?}", data); - self.core_contract_client.initialize_with(data).await.expect("Failed to initialize"); + self.core_contract_client.initialize_with(data).await.context("Failed to initialize")?; self.core_contract_client .register_operator(self.core_contract_client.client().address()) .await - .expect("Failed to register operator"); + .context("Failed to register operator")?; + Ok(()) } /// Add implementation Starknet core contract with the specified data. @@ -74,7 +76,7 @@ impl CoreContract for StarknetValidityContract { implementation_address: Address, verifier_address: Address, finalized: bool, - ) { + ) -> anyhow::Result<()> { let program_hash = StarkFelt(program_hash.to_bytes_be()); let init_data = @@ -92,9 +94,10 @@ impl CoreContract for StarknetValidityContract { self.core_contract_client .add_implementation(final_bytes, implementation_address, finalized) .await - .expect("Failed to call add implementation"); + .context("Failed to call add implementation")?; log::debug!("ℹī¸ add_implementation : done"); + Ok(()) } /// Add implementation Starknet core contract with the specified data. @@ -108,7 +111,7 @@ impl CoreContract for StarknetValidityContract { implementation_address: Address, verifier_address: Address, finalized: bool, - ) { + ) -> anyhow::Result<()> { let program_hash = StarkFelt(program_hash.to_bytes_be()); let init_data = @@ -126,45 +129,49 @@ impl CoreContract for StarknetValidityContract { self.core_contract_client .upgrade_to(final_bytes, implementation_address, finalized) .await - .expect("Failed to call upgrade to"); + .context("Failed to call upgrade_to")?; log::debug!("ℹī¸ upgrade_to : done"); + Ok(()) } /// For registering the operator for Starknet Core Contract - async fn register_operator_core_contract(&self, operator_address: Address) { - self.core_contract_client.register_operator(operator_address).await.expect("Failed to register operator"); + async fn register_operator_core_contract(&self, operator_address: Address) -> anyhow::Result<()> { + self.core_contract_client.register_operator(operator_address).await.context("Failed to register operator")?; log::debug!("ℹī¸ register_operator : done"); + Ok(()) } /// For nominating the governor for Starknet Core Contract - async fn nominate_governor_core_contract(&self, l1_governor_address: Address) { + async fn nominate_governor_core_contract(&self, l1_governor_address: Address) -> anyhow::Result<()> { self.core_contract_client .starknet_nominate_new_governor(l1_governor_address) .await - .expect("Failed to nominate governor"); + .context("Failed to nominate governor")?; log::debug!("ℹī¸ register_operator : done"); + Ok(()) } /// For nominating the governor for Starknet Core Contract Proxy - async fn nominate_governor_core_contract_proxy(&self, l1_governor_address: Address) { + async fn nominate_governor_core_contract_proxy(&self, l1_governor_address: Address) -> anyhow::Result<()> { self.core_contract_client .proxy_nominate_new_governor(l1_governor_address) .await - .expect("Failed to register operator"); + .context("Failed to register operator")?; log::debug!("ℹī¸ proxy_nominate_new_governor : done"); + Ok(()) } /// Initialize Starknet core contract with the specified program and config hashes. The rest of /// parameters will be left default. /// IMP : only need to be called when using unsafe proxy - async fn initialize(&self, program_hash: StarkFelt, config_hash: StarkFelt) { + async fn initialize(&self, program_hash: StarkFelt, config_hash: StarkFelt) -> anyhow::Result<()> { self.initialize_with(CoreContractInitData { program_hash: convert_felt_to_u256(program_hash), config_hash: convert_felt_to_u256(config_hash), ..Default::default() }) - .await; + .await } /// Initialize Starknet core contract with the specified block number and state root hash. @@ -176,12 +183,12 @@ impl CoreContract for StarknetValidityContract { program_hash: FieldElement, config_hash: StarkHash, verifer_address: Address, - ) { + ) -> anyhow::Result<()> { let program_hash = StarkFelt(program_hash.to_bytes_be()); let init_data = get_init_data_core_contract(block_number, state_root, program_hash, config_hash, verifer_address); - self.initialize_with(init_data).await; + self.initialize_with(init_data).await } } diff --git a/src/contract_clients/token_bridge.rs b/src/contract_clients/token_bridge.rs index a2577fd..d6854b7 100644 --- a/src/contract_clients/token_bridge.rs +++ b/src/contract_clients/token_bridge.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use std::time::Duration; +use anyhow::Context; use async_trait::async_trait; use ethers::addressbook::Address; use ethers::prelude::U256; @@ -47,36 +48,37 @@ pub struct StarknetTokenBridge { #[async_trait] impl BridgeDeployable for StarknetTokenBridge { - async fn deploy(client: Arc, is_dev: bool) -> Self { + async fn deploy(client: Arc, is_dev: bool) -> anyhow::Result { let manager = match is_dev { false => deploy_starkgate_manager_behind_safe_proxy(client.clone()) .await - .expect("Failed to deploy starkgate manager contract"), + .context("Failed to deploy starkgate manager contract")?, true => deploy_starkgate_manager_behind_unsafe_proxy(client.clone()) .await - .expect("Failed to deploy starkgate manager contract"), + .context("Failed to deploy starkgate manager contract")?, }; let registry = match is_dev { false => deploy_starkgate_registry_behind_safe_proxy(client.clone()) .await - .expect("Failed to deploy starkgate registry"), + .context("Failed to deploy starkgate registry")?, true => deploy_starkgate_registry_behind_unsafe_proxy(client.clone()) .await - .expect("Failed to deploy starkgate registry"), + .context("Failed to deploy starkgate registry")?, }; let token_bridge = match is_dev { false => deploy_starknet_token_bridge_behind_safe_proxy(client.clone()) .await - .expect("Failed to deploy starknet contract"), + .context("Failed to deploy starknet contract")?, true => deploy_starknet_token_bridge_behind_unsafe_proxy(client.clone()) .await - .expect("Failed to deploy starknet contract"), + .context("Failed to deploy starknet contract")?, }; - let erc20 = - deploy_dai_erc20_behind_unsafe_proxy(client.clone()).await.expect("Failed to deploy dai erc20 contract"); + let erc20 = deploy_dai_erc20_behind_unsafe_proxy(client.clone()) + .await + .context("Failed to deploy DAI erc20 contract")?; - Self { manager, registry, token_bridge, erc20 } + Ok(Self { manager, registry, token_bridge, erc20 }) } } @@ -111,18 +113,22 @@ impl StarknetTokenBridge { rpc_provider_l2: &JsonRpcClient, priv_key: &str, l2_deployer_address: &str, - ) -> FieldElement { - let account = build_single_owner_account(rpc_provider_l2, priv_key, l2_deployer_address, false).await; + ) -> anyhow::Result { + let account = build_single_owner_account(rpc_provider_l2, priv_key, l2_deployer_address, false) + .await + .context("Making the single owner account")?; let token_bridge_class_hash = declare_contract(DeclarationInput::DeclarationInputs( String::from(TOKEN_BRIDGE_SIERRA_PATH), String::from(TOKEN_BRIDGE_CASM_PATH), account.clone(), )) - .await; + .await + .context("Declaring token bridge contract")?; + sleep(Duration::from_secs(10)).await; save_to_json("L2_token_bridge_class_hash", &JsonValueType::StringType(token_bridge_class_hash.to_string())) - .unwrap(); + .context("Saving the l2 token bridge class hash to json")?; log::debug!("🌗 token_bridge_class_hash : {:?}", token_bridge_class_hash); @@ -139,29 +145,30 @@ impl StarknetTokenBridge { ], None, ) + .context("Making the deploy token bridge transaction")? .send() .await - .expect("Error deploying the l2 contract proxy."); + .context("Error deploying the l2 contract proxy")?; wait_for_transaction( account.provider(), deploy_contract_implementation_token_bridge.transaction_hash, "deploy_l2_contracts : deploy_contract : token bridge", ) .await - .unwrap(); + .context("Waiting for l2 contract proxy to be deployed")?; sleep(Duration::from_secs(10)).await; let address_token_bridge_impl = get_contract_address_from_deploy_tx(account.provider(), &deploy_contract_implementation_token_bridge) .await - .unwrap(); + .context("Getting the token bridge contract address")?; log::debug!("🌗 contract address (token bridge) : {:?}", address_token_bridge_impl); - address_token_bridge_impl + Ok(address_token_bridge_impl) } /// Initialize Starknet Token Bridge. /// IMP : only need to be called when using unsafe proxy - pub async fn initialize(&self, messaging_contract: Address, governor: Address) { + pub async fn initialize(&self, messaging_contract: Address, governor: Address) -> anyhow::Result<()> { let empty_bytes = [0u8; 32]; let mut manager_calldata = Vec::new(); @@ -178,23 +185,28 @@ impl StarknetTokenBridge { bridge_calldata.extend(pad_bytes(self.manager_address())); bridge_calldata.extend(pad_bytes(messaging_contract)); - self.manager.initialize(Bytes::from(manager_calldata)).await.expect("Failed to initialize starkgate manager"); + self.manager + .initialize(Bytes::from(manager_calldata)) + .await + .context("Failed to initialize starkgate manager")?; self.registry .initialize(Bytes::from(registry_calldata)) .await - .expect("Failed to initialize starkgate registry"); + .context("Failed to initialize starkgate registry")?; self.token_bridge .initialize(Bytes::from(bridge_calldata)) .await - .expect("Failed to initialize starknet token bridge"); + .context("Failed to initialize starknet token bridge")?; // registering app governor temporarily - self.token_bridge.register_app_role_admin(governor).await.unwrap(); - self.token_bridge.register_app_governor(governor).await.unwrap(); + self.token_bridge.register_app_role_admin(governor).await.context("Registering bridge app role admin")?; + self.token_bridge.register_app_governor(governor).await.context("Registering bridge app governor")?; + + Ok(()) } /// Add Implementation Starknet Token Bridge - pub async fn add_implementation_token_bridge(&self, messaging_contract: Address) { + pub async fn add_implementation_token_bridge(&self, messaging_contract: Address) -> anyhow::Result<()> { let empty_bytes = [0u8; 32]; let mut manager_calldata = Vec::new(); @@ -214,22 +226,23 @@ impl StarknetTokenBridge { self.manager .add_implementation(Bytes::from(manager_calldata.clone()), self.manager.implementation_address(), false) .await - .expect("Failed to initialize starkgate manager"); + .context("Failed to initialize starkgate manager")?; log::debug!("🎡 add_implementation_token_bridge : manager bytes : {:?}", Bytes::from(manager_calldata)); self.registry .add_implementation(Bytes::from(registry_calldata.clone()), self.registry.implementation_address(), false) .await - .expect("Failed to initialize starkgate registry"); + .context("Failed to initialize starkgate registry")?; log::debug!("🎡 add_implementation_token_bridge : registry bytes : {:?}", Bytes::from(registry_calldata)); self.token_bridge .add_implementation(Bytes::from(bridge_calldata.clone()), self.token_bridge.implementation_address(), false) .await - .expect("Failed to initialize eth bridge"); + .context("Failed to initialize eth bridge")?; log::debug!("🎡 add_implementation_token_bridge : token_bridge bytes : {:?}", Bytes::from(bridge_calldata)); + Ok(()) } /// Upgrade To Starknet Token Bridge - pub async fn upgrade_to_token_bridge(&self, messaging_contract: Address) { + pub async fn upgrade_to_token_bridge(&self, messaging_contract: Address) -> anyhow::Result<()> { let empty_bytes = [0u8; 32]; let mut manager_calldata = Vec::new(); @@ -249,41 +262,66 @@ impl StarknetTokenBridge { self.manager .upgrade_to(Bytes::from(manager_calldata.clone()), self.manager.implementation_address(), false) .await - .expect("Failed to initialize starkgate manager"); + .context("Failed to initialize starkgate manager")?; log::debug!("🎡 upgrade_to_token_bridge : manager bytes : {:?}", Bytes::from(manager_calldata)); self.registry .upgrade_to(Bytes::from(registry_calldata.clone()), self.registry.implementation_address(), false) .await - .expect("Failed to initialize starkgate registry"); + .context("Failed to initialize starkgate registry")?; log::debug!("🎡 upgrade_to_token_bridge : registry bytes : {:?}", Bytes::from(registry_calldata)); self.token_bridge .upgrade_to(Bytes::from(bridge_calldata.clone()), self.token_bridge.implementation_address(), false) .await - .expect("Failed to initialize eth bridge"); + .context("Failed to initialize eth bridge")?; log::debug!("🎡 upgrade_to_token_bridge : token_bridge bytes : {:?}", Bytes::from(bridge_calldata)); + Ok(()) } /// Sets up the Token bridge with the specified data - pub async fn setup_permissions_with_bridge_l1(&self, governor: Address, l1_multisig_address: Address) { + pub async fn setup_permissions_with_bridge_l1( + &self, + governor: Address, + l1_multisig_address: Address, + ) -> anyhow::Result<()> { // Register roles - self.token_bridge.register_app_governor(governor).await.unwrap(); - self.token_bridge.register_app_role_admin(governor).await.unwrap(); - self.token_bridge.register_security_admin(governor).await.unwrap(); - self.token_bridge.register_security_agent(governor).await.unwrap(); + self.token_bridge.register_app_governor(governor).await.context("Registering token bridge app governor")?; + self.token_bridge.register_app_role_admin(governor).await.context("Registering token bridge app role admin")?; + self.token_bridge.register_security_admin(governor).await.context("Registering token bridge security admin")?; + self.token_bridge.register_security_agent(governor).await.context("Registering token security agent")?; // Nominating a new governor with l1_multisig_address - self.token_bridge.register_app_governor(l1_multisig_address).await.unwrap(); - self.manager.register_app_governor(l1_multisig_address).await.unwrap(); - self.registry.register_app_governor(l1_multisig_address).await.unwrap(); - self.token_bridge.register_app_role_admin(l1_multisig_address).await.unwrap(); - self.manager.register_app_role_admin(l1_multisig_address).await.unwrap(); - self.registry.register_app_role_admin(l1_multisig_address).await.unwrap(); + self.token_bridge + .register_app_governor(l1_multisig_address) + .await + .context("Registering token bridge app governor")?; + self.manager.register_app_governor(l1_multisig_address).await.context("Registering manager app governor")?; + self.registry.register_app_governor(l1_multisig_address).await.context("Registering registry app governor")?; + self.token_bridge + .register_app_role_admin(l1_multisig_address) + .await + .context("Registering token admin app role admin")?; + self.manager + .register_app_role_admin(l1_multisig_address) + .await + .context("Registering manager app role admin")?; + self.registry + .register_app_role_admin(l1_multisig_address) + .await + .context("Registering registry app role admin")?; + Ok(()) } /// Deploys a test ERC20 token from L1 to L2 - pub async fn setup_l1_bridge(&self, fee: U256, l2_bridge: FieldElement) { - self.token_bridge.set_l2_token_bridge(field_element_to_u256(l2_bridge)).await.unwrap(); - self.manager.enroll_token_bridge(self.address(), fee).await.unwrap(); + pub async fn setup_l1_bridge(&self, fee: U256, l2_bridge: FieldElement) -> anyhow::Result<()> { + self.token_bridge + .set_l2_token_bridge(field_element_to_u256(l2_bridge)) + .await + .context("Setting l2 token bridge field")?; + self.manager + .enroll_token_bridge(self.address(), fee) + .await + .context("Failed to enroll token bridge in manager")?; + Ok(()) } pub async fn setup_l2_bridge( @@ -293,14 +331,11 @@ impl StarknetTokenBridge { l2_address: &str, account: &RpcAccount<'_>, erc20_class_hash: FieldElement, - ) { - let tx = invoke_contract( - l2_bridge, - "register_app_role_admin", - vec![FieldElement::from_hex_be(l2_address).unwrap()], - account, - ) - .await; + ) -> anyhow::Result<()> { + let l2_address = FieldElement::from_hex_be(l2_address).context("Parsing l2 address")?; + let tx = invoke_contract(l2_bridge, "register_app_role_admin", vec![l2_address], account) + .await + .context("Making the register_app_role_admin transacction")?; wait_for_transaction( rpc_provider_l2, @@ -308,16 +343,12 @@ impl StarknetTokenBridge { "setup_l2_bridge : token bridge : register_app_role_admin", ) .await - .unwrap(); + .context("Waiting for the register_app_role_admin transacction to settle")?; log::debug!("🌗 setup_l2_bridge : register_app_role_admin //"); - let tx = invoke_contract( - l2_bridge, - "register_app_governor", - vec![FieldElement::from_hex_be(l2_address).unwrap()], - account, - ) - .await; + let tx = invoke_contract(l2_bridge, "register_app_governor", vec![l2_address], account) + .await + .context("Making the register_app_governor transaction")?; wait_for_transaction( rpc_provider_l2, @@ -325,16 +356,12 @@ impl StarknetTokenBridge { "setup_l2_bridge : token bridge : register_app_governor", ) .await - .unwrap(); + .context("Waiting for the register_app_governor transaction to settle")?; log::debug!("🌗 setup_l2_bridge : register_app_governor //"); - let tx = invoke_contract( - l2_bridge, - "set_l2_token_governance", - vec![FieldElement::from_hex_be(l2_address).unwrap()], - account, - ) - .await; + let tx = invoke_contract(l2_bridge, "set_l2_token_governance", vec![l2_address], account) + .await + .context("Making the set_l2_token_governance transaction")?; wait_for_transaction( rpc_provider_l2, @@ -342,7 +369,7 @@ impl StarknetTokenBridge { "setup_l2_bridge : token bridge : set_l2_token_governance", ) .await - .unwrap(); + .context("Waiting for the set_l2_token_governance transaction to settle")?; log::debug!("🌗 setup_l2_bridge : set_l2_token_governance //"); let tx = invoke_contract( @@ -353,7 +380,8 @@ impl StarknetTokenBridge { ], account, ) - .await; + .await + .context("Making the set_erc20_class_hash transaction")?; wait_for_transaction( rpc_provider_l2, @@ -361,63 +389,84 @@ impl StarknetTokenBridge { "setup_l2_bridge : token bridge : set_erc20_class_hash", ) .await - .unwrap(); + .context("Waiting for the setup_l2_bridge transaction to settle")?; log::debug!("🌗 setup_l2_bridge : set_erc20_class_hash //"); let tx = invoke_contract( l2_bridge, "set_l1_bridge", - vec![FieldElement::from_byte_slice_be(self.token_bridge.address().as_bytes()).unwrap()], + vec![ + FieldElement::from_byte_slice_be(self.token_bridge.address().as_bytes()) + .context("Parsing token bridge address")?, + ], account, ) - .await; + .await + .context("Making the set_l1_bridge transaction")?; wait_for_transaction(rpc_provider_l2, tx.transaction_hash, "setup_l2_bridge : token bridge : set_l1_bridge") .await - .unwrap(); + .context("Waiting for the set_l1_bridge transaction to settle")?; log::debug!("🌗 setup_l2_bridge : set_l1_bridge //"); + Ok(()) } - pub async fn register_app_role_admin(&self, address: Address) { + pub async fn register_app_role_admin(&self, address: Address) -> anyhow::Result<()> { self.token_bridge .register_app_role_admin(address) .await - .expect("Failed to register app role admin in starknet token bridge"); + .context("Failed to register app role admin in starknet token bridge")?; + Ok(()) } - pub async fn register_app_governor(&self, address: Address) { + pub async fn register_app_governor(&self, address: Address) -> anyhow::Result<()> { self.token_bridge .register_app_governor(address) .await - .expect("Failed to register app governor in starknet token bridge"); + .context("Failed to register app governor in starknet token bridge")?; + Ok(()) } - pub async fn set_l2_token_bridge(&self, l2_bridge: U256) { + pub async fn set_l2_token_bridge(&self, l2_bridge: U256) -> anyhow::Result<()> { self.token_bridge .set_l2_token_bridge(l2_bridge) .await - .expect("Failed to set l2 bridge in starknet token bridge"); + .context("Failed to set l2 bridge in starknet token bridge")?; + Ok(()) } - pub async fn deposit(&self, token: Address, amount: U256, l2address: U256, fee: U256) { - self.token_bridge.deposit(token, amount, l2address, fee).await.expect("Failed to bridge funds from l1 to l2"); + pub async fn deposit(&self, token: Address, amount: U256, l2address: U256, fee: U256) -> anyhow::Result<()> { + self.token_bridge + .deposit(token, amount, l2address, fee) + .await + .context("Failed to bridge funds from l1 to l2")?; + Ok(()) } - pub async fn withdraw(&self, l1_token: Address, amount: U256, l1_recipient: Address) { + pub async fn withdraw(&self, l1_token: Address, amount: U256, l1_recipient: Address) -> anyhow::Result<()> { self.token_bridge .withdraw(l1_token, amount, l1_recipient) .await - .expect("Failed to withdraw from starknet token bridge"); + .context("Failed to withdraw from starknet token bridge")?; + Ok(()) } - pub async fn enroll_token_bridge(&self, address: Address, fee: U256) { - self.manager.enroll_token_bridge(address, fee).await.expect("Failed to enroll token in starknet token bridge"); + pub async fn enroll_token_bridge(&self, address: Address, fee: U256) -> anyhow::Result<()> { + self.manager + .enroll_token_bridge(address, fee) + .await + .context("Failed to enroll token in starknet token bridge")?; + Ok(()) } - pub async fn approve(&self, address: Address, amount: U256) { - self.erc20.approve(address, amount).await.expect("Failed to approve dai transfer for starknet token bridge"); + pub async fn approve(&self, address: Address, amount: U256) -> anyhow::Result<()> { + self.erc20 + .approve(address, amount) + .await + .context("Failed to approve dai transfer for starknet token bridge")?; + Ok(()) } - pub async fn token_balance(&self, address: Address) -> U256 { - self.erc20.balance_of(address).await.unwrap() + pub async fn token_balance(&self, address: Address) -> anyhow::Result { + self.erc20.balance_of(address).await.with_context(|| format!("Getting ERC20 balance for {address}")) } } diff --git a/src/contract_clients/utils.rs b/src/contract_clients/utils.rs index 187959b..3214976 100644 --- a/src/contract_clients/utils.rs +++ b/src/contract_clients/utils.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use anyhow::Context; use ethers::types::U256; use hex::encode; use serde::{Deserialize, Serialize}; @@ -25,34 +26,39 @@ pub async fn build_single_owner_account<'a>( private_key: &str, account_address: &str, is_legacy: bool, -) -> RpcAccount<'a> { - let signer = LocalWallet::from(SigningKey::from_secret_scalar(FieldElement::from_hex_be(private_key).unwrap())); - let account_address = FieldElement::from_hex_be(account_address).expect("Invalid Contract Address"); +) -> anyhow::Result> { + let signer = LocalWallet::from(SigningKey::from_secret_scalar( + FieldElement::from_hex_be(private_key).context("Parsing private key")?, + )); + let account_address = FieldElement::from_hex_be(account_address) + .with_context(|| format!("Invalid contract address: {account_address}"))?; let execution_encoding = if is_legacy { starknet_accounts::ExecutionEncoding::Legacy } else { starknet_accounts::ExecutionEncoding::New }; - let chain_id = rpc.chain_id().await.unwrap(); - SingleOwnerAccount::new(rpc, signer, account_address, chain_id, execution_encoding) + let chain_id = rpc.chain_id().await.context("Getting the chain id from RPC")?; + Ok(SingleOwnerAccount::new(rpc, signer, account_address, chain_id, execution_encoding)) } pub async fn read_erc20_balance( rpc: &JsonRpcClient, contract_address: FieldElement, account_address: FieldElement, -) -> Vec { +) -> anyhow::Result> { rpc.call( FunctionCall { contract_address, - entry_point_selector: get_selector_from_name("balanceOf").unwrap(), + entry_point_selector: get_selector_from_name("balanceOf")?, calldata: vec![account_address], }, BlockId::Tag(BlockTag::Latest), ) .await - .unwrap() + .with_context(|| { + format!("Reading the erc20 balance for {account_address:#x} on ERC20 contract {contract_address:#x}") + }) } pub fn field_element_to_u256(input: FieldElement) -> U256 { @@ -71,14 +77,16 @@ pub fn generate_config_hash( ]) } -pub fn get_bridge_init_configs(config: &CliArgs) -> (FieldElement, StarkHash) { - let program_hash = FieldElement::from_hex_be(config.sn_os_program_hash.as_str()).unwrap(); +pub fn get_bridge_init_configs(config: &CliArgs) -> anyhow::Result<(FieldElement, StarkHash)> { + let program_hash = + FieldElement::from_hex_be(config.sn_os_program_hash.as_str()).context("Parsing sn_os_program_hash")?; let config_hash = generate_config_hash( - FieldElement::from_hex_be(&encode(config.config_hash_version.as_str())).expect("error in config_hash_version"), - FieldElement::from_hex_be(&encode(config.app_chain_id.as_str())).expect("error in app_chain_id"), - FieldElement::from_hex_be(config.fee_token_address.as_str()).expect("error in fee_token_address"), + FieldElement::from_hex_be(&encode(config.config_hash_version.as_str())) + .context("Parsing config_hash_version")?, + FieldElement::from_hex_be(&encode(config.app_chain_id.as_str())).context("Parsing app_chain_id")?, + FieldElement::from_hex_be(config.fee_token_address.as_str()).context("Parsing fee_token_address")?, ); - (program_hash, config_hash) + Ok((program_hash, config_hash)) } /// Broadcasted declare contract transaction v0. @@ -114,29 +122,31 @@ pub(crate) enum DeclarationInput<'a> { } #[allow(private_interfaces)] -pub async fn declare_contract(input: DeclarationInput<'_>) -> FieldElement { +pub async fn declare_contract(input: DeclarationInput<'_>) -> anyhow::Result { match input { DeclarationInputs(sierra_path, casm_path, account) => { - let (class_hash, sierra) = account.declare_contract_params_sierra(&sierra_path, &casm_path); + let (class_hash, sierra) = account + .declare_contract_params_sierra(&sierra_path, &casm_path) + .context("Creating declare sierra transaction")?; account .declare(Arc::new(sierra.clone()), class_hash) .send() .await - .expect("Error in declaring the contract using Cairo 1 declaration using the provided account"); - sierra.class_hash() + .context("Error while declaring the contract using Cairo 1 declaration using the provided account")?; + Ok(sierra.class_hash()) } LegacyDeclarationInputs(artifact_path, url) => { - let contract_abi_artifact_temp: LegacyContractClass = serde_json::from_reader( - std::fs::File::open(env!("CARGO_MANIFEST_DIR").to_owned() + "/" + &artifact_path).unwrap(), - ) - .unwrap(); + let path = format!("{}/{}", env!("CARGO_MANIFEST_DIR").to_owned(), &artifact_path); + let contract_abi_artifact_temp: LegacyContractClass = + serde_json::from_reader(std::fs::File::open(&path).with_context(|| format!("Opening file {}", path))?) + .with_context(|| format!("Reading file {}", path))?; let contract_abi_artifact = - contract_abi_artifact_temp.clone().compress().expect("Error : Failed to compress the contract class"); + contract_abi_artifact_temp.clone().compress().context("Failed to compress the contract class")?; let params: BroadcastedDeclareTransactionV0 = BroadcastedDeclareTransactionV0 { - sender_address: FieldElement::from_hex_be("0x1").unwrap(), + sender_address: FieldElement::from_hex_be("0x1").expect("Parsing constant"), max_fee: FieldElement::from(482250u128), signature: Vec::new(), contract_class: Arc::new(contract_abi_artifact), @@ -151,22 +161,18 @@ pub async fn declare_contract(input: DeclarationInput<'_>) -> FieldElement { }); let req_client = reqwest::Client::new(); - let raw_txn_rpc = req_client.post(url).json(json_body).send().await; - - match raw_txn_rpc { - Ok(val) => { - log::debug!( - "🚧 Txn Sent Successfully : {:?}", - val.json::>().await.unwrap() - ); - } - Err(err) => { - log::debug!("Error : Error sending the transaction using RPC"); - log::debug!("{:?}", err); - } - } - - contract_abi_artifact_temp.class_hash().unwrap() + let raw_res: DeclareTransactionResult = req_client + .post(url) + .json(json_body) + .send() + .await + .context("Declaring contract via RPC")? + .json() + .await + .context("Parsing json response from RPC")?; + log::debug!("🚧 Txn Sent Successfully : {:?}", raw_res); + + contract_abi_artifact_temp.class_hash().context("Getting class hash from contract ABI") } } } @@ -175,27 +181,31 @@ pub(crate) async fn deploy_account_using_priv_key( priv_key: String, provider: &JsonRpcClient, oz_account_class_hash: FieldElement, -) -> FieldElement { - let chain_id = provider.chain_id().await.unwrap(); +) -> anyhow::Result { + let chain_id = provider.chain_id().await.context("Parsing chain id")?; let signer = Arc::new(LocalWallet::from_signing_key(SigningKey::from_secret_scalar( - FieldElement::from_hex_be(&priv_key).unwrap(), + FieldElement::from_hex_be(&priv_key).context("Parsing private key")?, ))); log::trace!("signer : {:?}", signer); - let mut oz_account_factory = - OpenZeppelinAccountFactory::new(oz_account_class_hash, chain_id, signer, provider).await.unwrap(); + let mut oz_account_factory = OpenZeppelinAccountFactory::new(oz_account_class_hash, chain_id, signer, provider) + .await + .context("Making OZ factory")?; oz_account_factory.set_block_id(BlockId::Tag(BlockTag::Pending)); let deploy_txn = oz_account_factory.deploy(FieldElement::ZERO); let account_address = deploy_txn.address(); log::trace!("OZ Account Deployed : {:?}", account_address); - save_to_json("account_address", &JsonValueType::StringType(account_address.to_string())).unwrap(); + save_to_json("account_address", &JsonValueType::StringType(account_address.to_string())) + .context("Saving deployed OZ account address to json")?; - let sent_txn = deploy_txn.send().await.expect("Error in deploying the OZ account"); + let sent_txn = deploy_txn.send().await.context("Deploying the OZ account")?; log::trace!("deploy account txn_hash : {:?}", sent_txn.transaction_hash); - wait_for_transaction(provider, sent_txn.transaction_hash, "deploy_account_using_priv_key").await.unwrap(); + wait_for_transaction(provider, sent_txn.transaction_hash, "deploy_account_using_priv_key") + .await + .context("Waiting for the OZ account to be deployed")?; - account_address + Ok(account_address) } pub(crate) async fn deploy_proxy_contract( @@ -204,7 +214,7 @@ pub(crate) async fn deploy_proxy_contract( class_hash: FieldElement, salt: FieldElement, deploy_from_zero: FieldElement, -) -> FieldElement { +) -> anyhow::Result { let txn = account .invoke_contract( account_address, @@ -212,23 +222,35 @@ pub(crate) async fn deploy_proxy_contract( vec![class_hash, salt, deploy_from_zero, FieldElement::ONE, FieldElement::ZERO], None, ) + .context("Making deploy_contract transaction")? .send() .await - .expect("Error deploying the contract proxy."); + .context("Error deploying the contract proxy")?; wait_for_transaction(account.provider(), txn.transaction_hash, "deploy_proxy_contract : deploy_contract") .await - .unwrap(); + .context("Waiting for the proxy contract deployment")?; log::trace!("txn hash (proxy deployment) : {:?}", txn.transaction_hash); - let deployed_address = get_contract_address_from_deploy_tx(account.provider(), &txn).await.unwrap(); + let deployed_address = get_contract_address_from_deploy_tx(account.provider(), &txn) + .await + .context("Getting contract address from deploy transaction")?; log::trace!("[IMP] Event : {:?}", deployed_address); - deployed_address + Ok(deployed_address) } -pub(crate) async fn init_governance_proxy(account: &'_ RpcAccount<'_>, contract_address: FieldElement, tag: &str) { - let txn = invoke_contract(contract_address, "init_governance", vec![], account).await; - wait_for_transaction(account.provider(), txn.transaction_hash, tag).await.unwrap(); +pub(crate) async fn init_governance_proxy( + account: &'_ RpcAccount<'_>, + contract_address: FieldElement, + tag: &str, +) -> anyhow::Result<()> { + let txn = invoke_contract(contract_address, "init_governance", vec![], account) + .await + .context("Making init_governance transaction")?; + wait_for_transaction(account.provider(), txn.transaction_hash, tag) + .await + .context("Waiting for init_governance transaction to settle")?; + Ok(()) } diff --git a/src/helpers/account_actions.rs b/src/helpers/account_actions.rs index f1e7107..4325385 100644 --- a/src/helpers/account_actions.rs +++ b/src/helpers/account_actions.rs @@ -1,7 +1,10 @@ use std::future::Future; +use std::path::Path; +use anyhow::{bail, Context}; use assert_matches::assert_matches; use async_trait::async_trait; +use serde::de::DeserializeOwned; use starknet_accounts::{Account, Call, Execution, SingleOwnerAccount}; use starknet_api::core::{calculate_contract_address, ClassHash, ContractAddress}; use starknet_api::transaction::{Calldata, ContractAddressSalt}; @@ -13,7 +16,7 @@ use starknet_core::types::{ use starknet_core::utils::get_selector_from_name; use starknet_ff::FieldElement; use starknet_providers::jsonrpc::HttpTransport; -use starknet_providers::{JsonRpcClient, Provider, ProviderError}; +use starknet_providers::{JsonRpcClient, Provider}; use starknet_signers::LocalWallet; use crate::contract_clients::utils::RpcAccount; @@ -35,14 +38,14 @@ pub trait AccountActions { method: &str, calldata: Vec, nonce: Option, - ) -> TransactionExecution; + ) -> anyhow::Result; fn declare_contract_params_sierra( &self, path_to_sierra: &str, path_to_casm: &str, - ) -> (FieldElement, FlattenedSierraClass); - fn declare_contract_params_legacy(&self, path_to_compiled_contract: &str) -> LegacyContractClass; + ) -> anyhow::Result<(FieldElement, FlattenedSierraClass)>; + fn declare_contract_params_legacy(&self, path_to_compiled_contract: &str) -> anyhow::Result; } impl AccountActions for SingleOwnerAccount<&JsonRpcClient, LocalWallet> { @@ -52,90 +55,103 @@ impl AccountActions for SingleOwnerAccount<&JsonRpcClient, LocalW method: &str, calldata: Vec, nonce: Option, - ) -> TransactionExecution { - let calls = vec![Call { to: address, selector: get_selector_from_name(method).unwrap(), calldata }]; + ) -> anyhow::Result { + let calls = vec![Call { + to: address, + selector: get_selector_from_name(method) + .with_context(|| format!("Getting function selector for method {method}"))?, + calldata, + }]; - let max_fee = FieldElement::from_hex_be(MAX_FEE_OVERRIDE).unwrap(); + let max_fee = FieldElement::from_hex_be(MAX_FEE_OVERRIDE).expect("Converting a constant to field element"); - match nonce { + Ok(match nonce { Some(nonce) => self.execute(calls).max_fee(max_fee).nonce(nonce.into()), None => self.execute(calls).max_fee(max_fee), - } + }) } fn declare_contract_params_sierra( &self, path_to_sierra: &str, path_to_casm: &str, - ) -> (FieldElement, FlattenedSierraClass) { - let sierra: SierraClass = serde_json::from_reader( - std::fs::File::open(env!("CARGO_MANIFEST_DIR").to_owned() + "/" + path_to_sierra).unwrap(), - ) - .unwrap(); - - let flattened_class = sierra.flatten().unwrap(); - - let casm: CompiledClass = serde_json::from_reader( - std::fs::File::open(env!("CARGO_MANIFEST_DIR").to_owned() + "/" + path_to_casm).unwrap(), - ) - .unwrap(); - - (casm.class_hash().unwrap(), flattened_class) + ) -> anyhow::Result<(FieldElement, FlattenedSierraClass)> { + let path = format!("{}/{}", env!("CARGO_MANIFEST_DIR").to_owned(), path_to_sierra); + let sierra: SierraClass = + deser_json_artifact(&path).with_context(|| format!("Loading Sierra artifact at path {}", path))?; + + let flattened_class = + sierra.flatten().with_context(|| format!("Flattening sierra class loaded from path {}", path_to_sierra))?; + + let path = format!("{}/{}", env!("CARGO_MANIFEST_DIR").to_owned(), path_to_casm); + let casm: CompiledClass = + deser_json_artifact(&path).with_context(|| format!("Loading compiled CASM artifact at path {}", path))?; + + Ok(( + casm.class_hash().with_context(|| format!("Getting class hash from casm artifact {}", path_to_casm))?, + flattened_class, + )) } - fn declare_contract_params_legacy(&self, path_to_compiled_contract: &str) -> LegacyContractClass { - let contract_artifact: LegacyContractClass = serde_json::from_reader( - std::fs::File::open(env!("CARGO_MANIFEST_DIR").to_owned() + "/" + path_to_compiled_contract).unwrap(), - ) - .unwrap(); - - contract_artifact + fn declare_contract_params_legacy(&self, path_to_compiled_contract: &str) -> anyhow::Result { + let path = format!("{}/{}", env!("CARGO_MANIFEST_DIR").to_owned(), path_to_compiled_contract); + let contract_artifact: LegacyContractClass = + deser_json_artifact(&path).with_context(|| format!("Loading legacy CASM artifact at path {}", path))?; + Ok(contract_artifact) } } -pub async fn assert_poll(f: F, polling_time_ms: u64, max_poll_count: u32) +fn deser_json_artifact(path: impl AsRef) -> anyhow::Result { + let res = serde_json::from_reader(std::fs::File::open(&path).context("Opening file")?) + .context("Reading and deserializing file")?; + Ok(res) +} + +pub async fn assert_poll(f: F, polling_time_ms: u64, max_poll_count: u32) -> anyhow::Result<()> where F: Fn() -> Fut, Fut: Future, { for _poll_count in 0..max_poll_count { if f().await { - return; // The provided function returned true, exit safely. + return Ok(()); // The provided function returned true, exit safely. } tokio::time::sleep(tokio::time::Duration::from_millis(polling_time_ms)).await; } - panic!("Max poll count exceeded."); + bail!("Max poll count exceeded"); } -type TransactionReceiptResult = Result; - pub async fn get_transaction_receipt( rpc: &JsonRpcClient, transaction_hash: FieldElement, -) -> TransactionReceiptResult { +) -> anyhow::Result { // there is a delay between the transaction being available at the client // and the sealing of the block, hence sleeping for 100ms - assert_poll(|| async { rpc.get_transaction_receipt(transaction_hash).await.is_ok() }, 100, 20).await; + assert_poll(|| async { rpc.get_transaction_receipt(transaction_hash).await.is_ok() }, 100, 20) + .await + .with_context(|| format!("Getting transaction receipt for transaction hash {:#x}", transaction_hash))?; - rpc.get_transaction_receipt(transaction_hash).await + rpc.get_transaction_receipt(transaction_hash) + .await + .with_context(|| format!("Getting transaction receipt for transaction hash {:#x}", transaction_hash)) } pub async fn get_contract_address_from_deploy_tx( rpc: &JsonRpcClient, tx: &InvokeTransactionResult, -) -> Result { +) -> anyhow::Result { let deploy_tx_hash = tx.transaction_hash; - wait_for_transaction(rpc, deploy_tx_hash, "get_contract_address_from_deploy_tx").await.unwrap(); + wait_for_transaction(rpc, deploy_tx_hash, "get_contract_address_from_deploy_tx").await?; let deploy_tx_receipt = get_transaction_receipt(rpc, deploy_tx_hash).await?; let contract_address = assert_matches!( deploy_tx_receipt, MaybePendingTransactionReceipt::Receipt(TransactionReceipt::Invoke(receipt)) => { - receipt.events.iter().find(|e| e.keys[0] == get_selector_from_name("ContractDeployed").unwrap()).unwrap().data[0] + receipt.events.iter().find(|e| e.keys[0] == get_selector_from_name("ContractDeployed").expect("Converting constant to function selector")).context("The RPC did not return any contract deployed event")?.data[0] } ); Ok(contract_address) @@ -146,8 +162,9 @@ pub async fn calculate_deployed_address( class_hash: ClassHash, calldata: &Calldata, deployer_address: ContractAddress, -) -> FieldElement { - let address = calculate_contract_address(salt, class_hash, calldata, deployer_address).unwrap(); +) -> anyhow::Result { + let address = calculate_contract_address(salt, class_hash, calldata, deployer_address) + .context("Calculating deployed contract address")?; let bytes = address.0.0.0; - FieldElement::from_bytes_be(&bytes).unwrap() + FieldElement::from_bytes_be(&bytes).context("Converting contract address to field element") } diff --git a/src/main.rs b/src/main.rs index 7ad97ae..3d27183 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +#![deny(clippy::unwrap_used)] + pub mod contract_clients; pub mod helpers; mod setup_scripts; @@ -5,6 +7,7 @@ mod setup_scripts; pub mod tests; pub mod utils; +use anyhow::Context; use clap::{ArgAction, Parser}; use dotenv::dotenv; use inline_colorization::*; @@ -71,13 +74,17 @@ pub struct CliArgs { } #[tokio::main] -pub async fn main() { - env_logger::init(); - dotenv().ok(); +pub async fn main() -> anyhow::Result<()> { + env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); + if let Err(err) = dotenv() { + // ignore dotenv errors, but do not fail silently. + log::warn!("Error while loading the .env file: {err:#}"); + } let args = CliArgs::parse(); - bootstrap(&args).await; + bootstrap(&args).await?; + Ok(()) } pub struct DeployBridgeOutput { @@ -96,21 +103,21 @@ pub struct DeployBridgeOutput { pub eth_bridge: StarknetLegacyEthBridge, } -pub async fn bootstrap(config: &CliArgs) -> DeployBridgeOutput { +pub async fn bootstrap(config: &CliArgs) -> anyhow::Result { println!("{color_red}{}{color_reset}", BANNER); - let clients = Config::init(config).await; + let clients = Config::init(config).await.context("Initializing client config")?; let core_contract = CoreContractStarknetL1::new(config, &clients); - let core_contract_client = core_contract.setup().await; + let core_contract_client = core_contract.setup().await.context("Setting up the L1 core contract")?; log::info!("đŸ“Ļ Core address : {:?}", core_contract_client.core_contract_client.address()); save_to_json( "l1_core_contract_address", &JsonValueType::EthAddress(core_contract_client.core_contract_client.address()), ) - .unwrap(); + .context("Saving l1 core contract addresses to json")?; log::info!("✅ Core setup init for L1 successful."); log::info!("âŗ L2 State and Initialisation Started"); - let account = account_init(&clients, config).await; - log::info!("🔐 Account with given private key deployed on L2. [Account Address : {:?}]", account.address()); + let account = account_init(&clients, config).await.context("Initializing L2 account")?; + log::info!("🔐 Account with given private key deployed on L2. [Account Address : {:?}]", account.address()); log::info!("âŗ Starting ETH bridge deployment"); let eth_bridge = EthBridge::new( account.clone(), @@ -119,7 +126,7 @@ pub async fn bootstrap(config: &CliArgs) -> DeployBridgeOutput { &clients, core_contract_client.core_contract_client.as_ref(), ); - let eth_bridge_setup_outputs = eth_bridge.setup().await; + let eth_bridge_setup_outputs = eth_bridge.setup().await.context("Setting up ETH bridge")?; log::info!("✅ ETH bridge deployment complete."); log::info!("âŗ Starting ERC20 token bridge deployment"); let erc20_bridge = Erc20Bridge::new( @@ -129,11 +136,11 @@ pub async fn bootstrap(config: &CliArgs) -> DeployBridgeOutput { &clients, core_contract_client.core_contract_client.as_ref(), ); - let erc20_bridge_setup_outputs = erc20_bridge.setup().await; + let erc20_bridge_setup_outputs = erc20_bridge.setup().await.context("Setting up ETH token bridge")?; log::info!("✅ ERC20 token bridge deployment complete."); log::info!("âŗ Starting UDC (Universal Deployer Contract) deployment"); let udc = UdcSetup::new(account.clone(), account.address(), config); - let udc_setup_outputs = udc.setup().await; + let udc_setup_outputs = udc.setup().await.context("Setting up UDC")?; log::info!( "*ī¸âƒŖ UDC setup completed. [UDC Address : {:?}, UDC class hash : {:?}]", udc_setup_outputs.udc_address, @@ -142,19 +149,19 @@ pub async fn bootstrap(config: &CliArgs) -> DeployBridgeOutput { log::info!("✅ UDC (Universal Deployer Contract) deployment complete."); log::info!("âŗ Starting Argent Account declaration"); let argent = ArgentSetup::new(account.clone()); - let argent_setup_outputs = argent.setup().await; + let argent_setup_outputs = argent.setup().await.context("Setting up Argent")?; log::info!("*ī¸âƒŖ Argent setup completed. [Argent account class hash : {:?}]", argent_setup_outputs.argent_class_hash); log::info!("✅ Argent Account declaration complete."); log::info!("âŗ Starting Braavos Account declaration"); let braavos = BraavosSetup::new(account.clone(), config); - let braavos_setup_outputs = braavos.setup().await; + let braavos_setup_outputs = braavos.setup().await.context("Setting up Braavos")?; log::info!( "*ī¸âƒŖ Braavos setup completed. [Braavos account class hash : {:?}]", braavos_setup_outputs.braavos_class_hash ); log::info!("✅ Braavos Account declaration complete."); - DeployBridgeOutput { + Ok(DeployBridgeOutput { starknet_contract: core_contract_client.core_contract_client, starknet_token_bridge: erc20_bridge_setup_outputs.starknet_token_bridge, erc20_class_hash: erc20_bridge_setup_outputs.erc20_cairo_one_class_hash, @@ -168,5 +175,5 @@ pub async fn bootstrap(config: &CliArgs) -> DeployBridgeOutput { erc20_l2_bridge_address: erc20_bridge_setup_outputs.erc20_l2_bridge_address, l2_erc20_token_address: erc20_bridge_setup_outputs.l2_erc20_token_address, eth_bridge: eth_bridge_setup_outputs.eth_bridge, - } + }) } diff --git a/src/setup_scripts/account_setup.rs b/src/setup_scripts/account_setup.rs index 1b70c09..7ee59cb 100644 --- a/src/setup_scripts/account_setup.rs +++ b/src/setup_scripts/account_setup.rs @@ -1,5 +1,6 @@ use std::time::Duration; +use anyhow::Context; use tokio::time::sleep; use crate::contract_clients::config::Config; @@ -11,16 +12,18 @@ use crate::utils::constants::{OZ_ACCOUNT_CASM_PATH, OZ_ACCOUNT_PATH, OZ_ACCOUNT_ use crate::utils::{convert_to_hex, save_to_json, JsonValueType}; use crate::CliArgs; -pub async fn account_init<'a>(clients: &'a Config, arg_config: &'a CliArgs) -> RpcAccount<'a> { +pub async fn account_init<'a>(clients: &'a Config, arg_config: &'a CliArgs) -> anyhow::Result> { // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // Making temp account for declaration of OZ account Cairo 1 contract let oz_account_class_hash = declare_contract(DeclarationInput::LegacyDeclarationInputs( String::from(OZ_ACCOUNT_PATH), arg_config.rollup_seq_url.clone(), )) - .await; + .await + .context("Declaring OZ class")?; log::debug!("OZ Account Class Hash Declared"); - save_to_json("oz_account_class_hash", &JsonValueType::StringType(oz_account_class_hash.to_string())).unwrap(); + save_to_json("oz_account_class_hash", &JsonValueType::StringType(oz_account_class_hash.to_string())) + .context("Saving OZ class hash to json")?; sleep(Duration::from_secs(10)).await; log::debug!("Waiting for block to be mined [/]"); @@ -28,24 +31,27 @@ pub async fn account_init<'a>(clients: &'a Config, arg_config: &'a CliArgs) -> R let account_address_temp = deploy_account_using_priv_key(TEMP_ACCOUNT_PRIV_KEY.to_string(), clients.provider_l2(), oz_account_class_hash) - .await; + .await + .context("Deploying temp OZ account")?; sleep(Duration::from_secs(10)).await; let user_account_temp = build_single_owner_account( clients.provider_l2(), TEMP_ACCOUNT_PRIV_KEY, - &convert_to_hex(&account_address_temp.to_string()), + &convert_to_hex(&account_address_temp.to_string())?, false, ) - .await; + .await + .context("Making OZ single owner account")?; let oz_account_caio_1_class_hash = declare_contract(DeclarationInput::DeclarationInputs( String::from(OZ_ACCOUNT_SIERRA_PATH), String::from(OZ_ACCOUNT_CASM_PATH), user_account_temp.clone(), )) - .await; + .await + .context("Declaring cairo 1 OZ class")?; save_to_json("oz_account_caio_1_class_hash", &JsonValueType::StringType(oz_account_caio_1_class_hash.to_string())) - .unwrap(); + .context("Saving OZ cairo 1 class hash to json")?; sleep(Duration::from_secs(10)).await; // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -56,14 +62,17 @@ pub async fn account_init<'a>(clients: &'a Config, arg_config: &'a CliArgs) -> R clients.provider_l2(), oz_account_caio_1_class_hash, ) - .await; - save_to_json("account_address", &JsonValueType::StringType(account_address.to_string())).unwrap(); + .await + .context("Deploying OZ cairo 1 contract")?; + save_to_json("account_address", &JsonValueType::StringType(account_address.to_string())) + .context("Saving OZ cairo 1 account address to json")?; build_single_owner_account( clients.provider_l2(), &arg_config.rollup_priv_key, - &convert_to_hex(&account_address.to_string()), + &convert_to_hex(&account_address.to_string())?, false, ) .await + .context("Creating single owner account") // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> } diff --git a/src/setup_scripts/argent.rs b/src/setup_scripts/argent.rs index 09f01c0..f368522 100644 --- a/src/setup_scripts/argent.rs +++ b/src/setup_scripts/argent.rs @@ -1,5 +1,6 @@ use std::time::Duration; +use anyhow::Context; use starknet_ff::FieldElement; use tokio::time::sleep; @@ -20,17 +21,19 @@ impl<'a> ArgentSetup<'a> { Self { account } } - pub async fn setup(&self) -> ArgentSetupOutput { + pub async fn setup(&self) -> anyhow::Result { let argent_class_hash = declare_contract(DeclarationInput::DeclarationInputs( String::from(ARGENT_ACCOUNT_SIERRA_PATH), String::from(ARGENT_ACCOUNT_CASM_PATH), self.account.clone(), )) - .await; + .await + .context("Declaring argent class")?; log::debug!("đŸ“Ŗ Argent Hash Declared"); - save_to_json("argent_class_hash", &JsonValueType::StringType(argent_class_hash.to_string())).unwrap(); + save_to_json("argent_class_hash", &JsonValueType::StringType(argent_class_hash.to_string())) + .context("Saving argent class hash to json")?; sleep(Duration::from_secs(10)).await; - ArgentSetupOutput { argent_class_hash } + Ok(ArgentSetupOutput { argent_class_hash }) } } diff --git a/src/setup_scripts/braavos.rs b/src/setup_scripts/braavos.rs index 750139a..9090995 100644 --- a/src/setup_scripts/braavos.rs +++ b/src/setup_scripts/braavos.rs @@ -1,5 +1,6 @@ use std::time::Duration; +use anyhow::Context; use starknet_accounts::{Account, ConnectedAccount}; use starknet_ff::FieldElement; use tokio::time::sleep; @@ -27,15 +28,17 @@ impl<'a> BraavosSetup<'a> { Self { account, arg_config } } - pub async fn setup(&self) -> BraavosSetupOutput { + pub async fn setup(&self) -> anyhow::Result { let braavos_class_hash = declare_contract(DeclarationInput::DeclarationInputs( String::from(BRAAVOS_ACCOUNT_SIERRA_PATH), String::from(BRAAVOS_ACCOUNT_CASM_PATH), self.account.clone(), )) - .await; + .await + .context("Declaring braavos class")?; log::debug!("đŸ“Ŗ Braavos Account class hash declared."); - save_to_json("braavos_class_hash", &JsonValueType::StringType(braavos_class_hash.to_string())).unwrap(); + save_to_json("braavos_class_hash", &JsonValueType::StringType(braavos_class_hash.to_string())) + .context("Saving braavos class hash to json")?; sleep(Duration::from_secs(10)).await; let braavos_base_account_class_hash = declare_contract(DeclarationInput::DeclarationInputs( @@ -43,26 +46,28 @@ impl<'a> BraavosSetup<'a> { String::from(BRAAVOS_BASE_ACCOUNT_CASM_PATH), self.account.clone(), )) - .await; + .await + .context("Declaring braavos base account class")?; log::debug!("đŸ“Ŗ Braavos Base Account class hash declared."); save_to_json( "braavos_base_account_class_hash", &JsonValueType::StringType(braavos_base_account_class_hash.to_string()), ) - .unwrap(); + .context("Saving braavos base account class hash to json")?; sleep(Duration::from_secs(10)).await; let braavos_aggregator_class_hash = declare_contract(DeclarationInput::LegacyDeclarationInputs( String::from(BRAAVOS_AGGREGATOR_PATH), self.arg_config.rollup_seq_url.clone(), )) - .await; + .await + .context("Declaring braavos aggregator class")?; log::debug!("đŸ“Ŗ Braavos Aggregator class hash declared."); save_to_json( "braavos_aggregator_class_hash", &JsonValueType::StringType(braavos_aggregator_class_hash.to_string()), ) - .unwrap(); + .context("Saving braavos aggregator class hash to json")?; sleep(Duration::from_secs(10)).await; let deploy_tx = self @@ -73,16 +78,19 @@ impl<'a> BraavosSetup<'a> { vec![braavos_aggregator_class_hash, FieldElement::ZERO, FieldElement::ZERO, FieldElement::ZERO], None, ) + .context("Making braavos deploy_contract transaction")? .send() .await - .expect("Error deploying the contract proxy."); + .context("Error deploying the contract proxy")?; wait_for_transaction(self.account.provider(), deploy_tx.transaction_hash, "deploy_eth_token_on_l2 : deploy") .await - .unwrap(); - let contract_address = get_contract_address_from_deploy_tx(self.account.provider(), &deploy_tx).await.unwrap(); + .context("Waiting for braavos deploy_contract transaction to settle")?; + let contract_address = get_contract_address_from_deploy_tx(self.account.provider(), &deploy_tx) + .await + .context("Getting resulting braavos contract address")?; log::info!("*ī¸âƒŖ Braavos Aggregator deployed. [Braavos Aggregator : {:?}]", contract_address); - BraavosSetupOutput { braavos_class_hash } + Ok(BraavosSetupOutput { braavos_class_hash }) } } diff --git a/src/setup_scripts/core_contract.rs b/src/setup_scripts/core_contract.rs index ad70a35..7cd17c4 100644 --- a/src/setup_scripts/core_contract.rs +++ b/src/setup_scripts/core_contract.rs @@ -1,5 +1,6 @@ use std::str::FromStr; +use anyhow::Context; use ethers::abi::Address; use starknet_api::hash::StarkFelt; @@ -25,17 +26,28 @@ impl<'a> CoreContractStarknetL1<'a> { Self { arg_config, clients } } - pub async fn setup(&self) -> CoreContractStarknetL1Output { + pub async fn setup(&self) -> anyhow::Result { let core_contract_client: Box = match self.arg_config.dev { - true => Box::new(StarknetSovereignContract::deploy(self.clients).await), - false => Box::new(StarknetValidityContract::deploy(self.clients).await), + true => Box::new( + StarknetSovereignContract::deploy(self.clients) + .await + .context("Deploying starknet sovereign contract")?, + ), + false => Box::new( + StarknetValidityContract::deploy(self.clients).await.context("Deploying starknet validity contract")?, + ), }; log::info!("đŸ“Ļ Core address : {:?}", core_contract_client.address()); - save_to_json("l1_core_contract_address", &JsonValueType::EthAddress(core_contract_client.address())).unwrap(); - let (program_hash, config_hash) = get_bridge_init_configs(self.arg_config); + save_to_json("l1_core_contract_address", &JsonValueType::EthAddress(core_contract_client.address())) + .context("Saving the l1 core contract address to json")?; + let (program_hash, config_hash) = + get_bridge_init_configs(self.arg_config).context("Getting bridge initialization config")?; if self.arg_config.dev { - core_contract_client.initialize(StarkFelt(program_hash.to_bytes_be()), config_hash).await; + core_contract_client + .initialize(StarkFelt(program_hash.to_bytes_be()), config_hash) + .await + .context("Initializing dev core contract client")?; } else { core_contract_client .add_implementation_core_contract( @@ -44,10 +56,11 @@ impl<'a> CoreContractStarknetL1<'a> { program_hash, config_hash, core_contract_client.implementation_address(), - Address::from_str(&self.arg_config.verifier_address.clone()).unwrap(), + Address::from_str(&self.arg_config.verifier_address.clone()).context("Parsing verifier_address")?, false, ) - .await; + .await + .context("Adding implementation core contract")?; core_contract_client .upgrade_to_core_contract( 0u64.into(), @@ -55,25 +68,31 @@ impl<'a> CoreContractStarknetL1<'a> { program_hash, config_hash, core_contract_client.implementation_address(), - Address::from_str(&self.arg_config.verifier_address.clone()).unwrap(), + Address::from_str(&self.arg_config.verifier_address.clone()).context("Parsing verifier_address")?, false, ) - .await; - core_contract_client - .register_operator_core_contract(Address::from_str(&self.arg_config.operator_address.clone()).unwrap()) - .await; + .await + .context("Upgrading core contract")?; core_contract_client - .nominate_governor_core_contract( - Address::from_str(&self.arg_config.l1_multisig_address.clone()).unwrap(), + .register_operator_core_contract( + Address::from_str(&self.arg_config.operator_address.clone()).context("Parsing operator_address")?, ) - .await; + .await + .context("Registering operator address")?; + + let l1_multisig_addr = Address::from_str(&self.arg_config.l1_multisig_address.clone()) + .context("Parsing L1 multisig address")?; + core_contract_client - .nominate_governor_core_contract_proxy( - Address::from_str(&self.arg_config.l1_multisig_address.clone()).unwrap(), - ) - .await; + .nominate_governor_core_contract(l1_multisig_addr) + .await + .context("Nominator governor")?; + core_contract_client + .nominate_governor_core_contract_proxy(l1_multisig_addr) + .await + .context("Nominator governor contract proxy")?; } - CoreContractStarknetL1Output { core_contract_client } + Ok(CoreContractStarknetL1Output { core_contract_client }) } } diff --git a/src/setup_scripts/erc20_bridge.rs b/src/setup_scripts/erc20_bridge.rs index 4f5fdd7..069104f 100644 --- a/src/setup_scripts/erc20_bridge.rs +++ b/src/setup_scripts/erc20_bridge.rs @@ -1,6 +1,7 @@ use std::str::FromStr; use std::time::Duration; +use anyhow::Context; use ethers::abi::Address; use ethers::prelude::{H160, U256}; use starknet_core::types::{BlockId, BlockTag, FunctionCall}; @@ -45,86 +46,113 @@ impl<'a> Erc20Bridge<'a> { Self { account, account_address, arg_config, clients, core_contract } } - pub async fn setup(&self) -> Erc20BridgeSetupOutput { + pub async fn setup(&self) -> anyhow::Result { let erc20_cairo_one_class_hash = declare_contract(DeclarationInput::DeclarationInputs( String::from(ERC20_SIERRA_PATH), String::from(ERC20_CASM_PATH), self.account.clone(), )) - .await; + .await + .context("Creating cairo 1 erc20 declare class transaction")?; log::debug!("🌗 ERC20 Class Hash declared : {:?}", erc20_cairo_one_class_hash); save_to_json("erc20_cairo_one_class_hash", &JsonValueType::StringType(erc20_cairo_one_class_hash.to_string())) - .unwrap(); + .context("Saving erc20 cairo 1 class hash to json")?; sleep(Duration::from_secs(10)).await; - let token_bridge = StarknetTokenBridge::deploy(self.core_contract.client().clone(), self.arg_config.dev).await; + let token_bridge = StarknetTokenBridge::deploy(self.core_contract.client().clone(), self.arg_config.dev) + .await + .context("Deploying token bridge")?; log::info!( "❇ī¸ ERC20 Token Bridge L1 deployment completed [ERC20 Token Bridge Address (L1) : {:?}]", token_bridge.bridge_address() ); - save_to_json("ERC20_l1_bridge_address", &JsonValueType::EthAddress(token_bridge.bridge_address())).unwrap(); - save_to_json("ERC20_l1_registry_address", &JsonValueType::EthAddress(token_bridge.registry_address())).unwrap(); - save_to_json("ERC20_l1_manager_address", &JsonValueType::EthAddress(token_bridge.manager_address())).unwrap(); + save_to_json("ERC20_l1_bridge_address", &JsonValueType::EthAddress(token_bridge.bridge_address())) + .context("Saving ERC20 L1 to bridge address json")?; + save_to_json("ERC20_l1_registry_address", &JsonValueType::EthAddress(token_bridge.registry_address())) + .context("Saving ERC20 L1 registry address to json")?; + save_to_json("ERC20_l1_manager_address", &JsonValueType::EthAddress(token_bridge.manager_address())) + .context("Saving ERC20 L1 manager address to json")?; let l2_bridge_address = StarknetTokenBridge::deploy_l2_contracts( self.clients.provider_l2(), &self.arg_config.rollup_priv_key, - &convert_to_hex(&self.account_address.to_string()), + &convert_to_hex(&self.account_address.to_string())?, ) - .await; + .await + .context("Deploying L2 contracts")?; log::info!( "❇ī¸ ERC20 Token Bridge L2 deployment completed [ERC20 Token Bridge Address (L2) : {:?}]", l2_bridge_address ); - save_to_json("ERC20_l2_bridge_address", &JsonValueType::StringType(l2_bridge_address.to_string())).unwrap(); + save_to_json("ERC20_l2_bridge_address", &JsonValueType::StringType(l2_bridge_address.to_string())) + .context("Saving l2 bridge address to json")?; let provider_l2 = self.clients.provider_l2(); let account = build_single_owner_account( provider_l2, &self.arg_config.rollup_priv_key, - &convert_to_hex(&self.account_address.to_string()), + &convert_to_hex(&self.account_address.to_string())?, false, ) - .await; + .await + .context("Making single owner account")?; + + let l1_deployer_address = + H160::from_str(&self.arg_config.l1_deployer_address).context("Parsing L1 deployer address")?; if self.arg_config.dev { token_bridge - .initialize(self.core_contract.address(), H160::from_str(&self.arg_config.l1_deployer_address).unwrap()) - .await; + .initialize(self.core_contract.address(), l1_deployer_address) + .await + .context("Initializing token bridge in dev mode")?; } else { - token_bridge.add_implementation_token_bridge(self.core_contract.address()).await; - token_bridge.upgrade_to_token_bridge(self.core_contract.address()).await; + token_bridge + .add_implementation_token_bridge(self.core_contract.address()) + .await + .context("Adding implementation token bridge")?; + token_bridge + .upgrade_to_token_bridge(self.core_contract.address()) + .await + .context("Upgrading token bridge")?; token_bridge .setup_permissions_with_bridge_l1( - H160::from_str(&self.arg_config.l1_deployer_address).unwrap(), - Address::from_str(&self.arg_config.l1_multisig_address.to_string()).unwrap(), + l1_deployer_address, + Address::from_str(&self.arg_config.l1_multisig_address.to_string()) + .context("Parsing L1 multisig address")?, ) - .await; + .await + .context("Setting up permissions with L1 bridge")?; } token_bridge .setup_l2_bridge( self.clients.provider_l2(), l2_bridge_address, - &convert_to_hex(&self.account_address.to_string()), + &convert_to_hex(&self.account_address.to_string())?, &account, erc20_cairo_one_class_hash, ) - .await; - token_bridge.setup_l1_bridge(U256::from_dec_str("100000000000000").unwrap(), l2_bridge_address).await; + .await + .context("Setting up L2 bridge")?; + token_bridge + .setup_l1_bridge(U256::from_dec_str("100000000000000").expect("Parsing a constant"), l2_bridge_address) + .await + .context("Setting up L1 temp test token bridge")?; log::info!("❇ī¸ Temp test token deployed on L1."); log::info!( "❇ī¸ Waiting for temp test token to be deployed on L2 [âŗ....] Approx. time : {:?} secs.", self.arg_config.cross_chain_wait_time + 10_u64 ); - sleep(Duration::from_secs(self.arg_config.l1_wait_time.parse().unwrap())).await; + sleep(Duration::from_secs(self.arg_config.l1_wait_time.parse().context("Parsing L1 wait time")?)).await; // We need to wait a little bit more for message to be consumed and executed sleep(Duration::from_secs(self.arg_config.cross_chain_wait_time)).await; let l2_erc20_token_address = - get_l2_token_address(self.clients.provider_l2(), &l2_bridge_address, &token_bridge.address()).await; + get_l2_token_address(self.clients.provider_l2(), &l2_bridge_address, &token_bridge.address()) + .await + .context("Getting L2 erc20 token address")?; log::info!( "❇ī¸ L2 ERC20 Token Address deployed for testing [ ERC20 Test Token Address : {:?}]", l2_erc20_token_address @@ -133,14 +161,14 @@ impl<'a> Erc20Bridge<'a> { "ERC20_l2_token_address_temp_test", &JsonValueType::StringType(l2_erc20_token_address.to_string()), ) - .unwrap(); + .context("Saving L2 erc20 token address temp test to json")?; - Erc20BridgeSetupOutput { + Ok(Erc20BridgeSetupOutput { erc20_cairo_one_class_hash, starknet_token_bridge: token_bridge, erc20_l2_bridge_address: l2_bridge_address, l2_erc20_token_address, - } + }) } } @@ -148,16 +176,22 @@ async fn get_l2_token_address( rpc_provider_l2: &JsonRpcClient, l2_bridge_address: &FieldElement, l1_erc_20_address: &H160, -) -> FieldElement { +) -> anyhow::Result { rpc_provider_l2 .call( FunctionCall { contract_address: *l2_bridge_address, - entry_point_selector: get_selector_from_name("get_l2_token").unwrap(), - calldata: vec![FieldElement::from_byte_slice_be(l1_erc_20_address.as_bytes()).unwrap()], + entry_point_selector: get_selector_from_name("get_l2_token")?, + calldata: vec![ + FieldElement::from_byte_slice_be(l1_erc_20_address.as_bytes()) + .context("Parsing l1 erc20 address")?, + ], }, BlockId::Tag(BlockTag::Pending), ) .await - .unwrap()[0] + .context("Calling get_l2_token")? + .first() + .copied() + .context("RPC did not return any result to call invocation") } diff --git a/src/setup_scripts/eth_bridge.rs b/src/setup_scripts/eth_bridge.rs index 283cfb7..781e538 100644 --- a/src/setup_scripts/eth_bridge.rs +++ b/src/setup_scripts/eth_bridge.rs @@ -1,6 +1,7 @@ use std::str::FromStr; use std::time::Duration; +use anyhow::Context; use ethers::abi::Address; use starknet_accounts::{Account, ConnectedAccount}; use starknet_ff::FieldElement; @@ -49,48 +50,52 @@ impl<'a> EthBridge<'a> { Self { account, account_address, arg_config, clients, core_contract } } - pub async fn setup(&self) -> EthBridgeSetupOutput { + pub async fn setup(&self) -> anyhow::Result { let legacy_proxy_class_hash = declare_contract(DeclarationInput::LegacyDeclarationInputs( String::from(PROXY_LEGACY_PATH), self.arg_config.rollup_seq_url.clone(), )) - .await; + .await + .context("Declaring legacy proxy class")?; log::debug!("🎡 Legacy proxy class hash declared."); save_to_json("legacy_proxy_class_hash", &JsonValueType::StringType(legacy_proxy_class_hash.to_string())) - .unwrap(); + .context("Saving legacy proxy class hash to json")?; sleep(Duration::from_secs(10)).await; let starkgate_proxy_class_hash = declare_contract(DeclarationInput::LegacyDeclarationInputs( String::from(STARKGATE_PROXY_PATH), self.arg_config.rollup_seq_url.clone(), )) - .await; + .await + .context("Declaring starkgate proxy class")?; log::debug!("🎡 Starkgate proxy class hash declared."); save_to_json("starkgate_proxy_class_hash", &JsonValueType::StringType(starkgate_proxy_class_hash.to_string())) - .unwrap(); + .context("Saving starkget proxy class hash to json")?; sleep(Duration::from_secs(10)).await; let erc20_legacy_class_hash = declare_contract(DeclarationInput::LegacyDeclarationInputs( String::from(ERC20_LEGACY_PATH), self.arg_config.rollup_seq_url.clone(), )) - .await; + .await + .context("Declaring erc20 legacy class")?; log::debug!("🎡 ERC20 legacy class hash declared."); save_to_json("erc20_legacy_class_hash", &JsonValueType::StringType(erc20_legacy_class_hash.to_string())) - .unwrap(); + .context("Saving erc20 legacy class hash to json")?; sleep(Duration::from_secs(10)).await; let legacy_eth_bridge_class_hash = declare_contract(DeclarationInput::LegacyDeclarationInputs( String::from(LEGACY_BRIDGE_PATH), self.arg_config.rollup_seq_url.clone(), )) - .await; + .await + .context("Declaring legacy eth bridge class")?; log::debug!("🎡 Legacy ETH Bridge class hash declared"); save_to_json( "legacy_eth_bridge_class_hash", &JsonValueType::StringType(legacy_eth_bridge_class_hash.to_string()), ) - .unwrap(); + .context("Saving legacy eth bridge class hash to json")?; sleep(Duration::from_secs(10)).await; let eth_proxy_address = deploy_proxy_contract( @@ -98,50 +103,60 @@ impl<'a> EthBridge<'a> { self.account_address, legacy_proxy_class_hash, // salt taken from : https://sepolia.starkscan.co/tx/0x06a5a493cf33919e58aa4c75777bffdef97c0e39cac968896d7bee8cc67905a1 - FieldElement::from_str("0x322c2610264639f6b2cee681ac53fa65c37e187ea24292d1b21d859c55e1a78").unwrap(), + FieldElement::from_str("0x322c2610264639f6b2cee681ac53fa65c37e187ea24292d1b21d859c55e1a78") + .expect("Parsing a constant"), FieldElement::ONE, ) - .await; + .await + .context("Deploying ETH ERC20 proxy contract")?; log::info!("✴ī¸ ETH ERC20 proxy deployed [ETH : {:?}]", eth_proxy_address); - save_to_json("l2_eth_address_proxy", &JsonValueType::StringType(eth_proxy_address.to_string())).unwrap(); + save_to_json("l2_eth_address_proxy", &JsonValueType::StringType(eth_proxy_address.to_string())) + .context("Saving ETH ERC20 proxy contract address to json")?; sleep(Duration::from_secs(10)).await; let eth_bridge_proxy_address = deploy_proxy_contract( &self.account, self.account_address, legacy_proxy_class_hash, - FieldElement::from_str("0xabcdabcdabcd").unwrap(), + FieldElement::from_str("0xabcdabcdabcd").expect("Parsing constant"), FieldElement::ZERO, ) - .await; + .await + .context("Deploying ETH brudge proxy contract")?; log::info!("✴ī¸ ETH Bridge proxy deployed [ETH Bridge : {:?}]", eth_bridge_proxy_address); save_to_json("ETH_l2_bridge_address_proxy", &JsonValueType::StringType(eth_bridge_proxy_address.to_string())) - .unwrap(); + .context("Saving ETH brudge proxy contract to json")?; sleep(Duration::from_secs(10)).await; - init_governance_proxy(&self.account, eth_proxy_address, "eth_proxy_address : init_governance_proxy").await; + init_governance_proxy(&self.account, eth_proxy_address, "eth_proxy_address : init_governance_proxy") + .await + .context("Initializing governance proxy")?; sleep(Duration::from_secs(10)).await; init_governance_proxy( &self.account, eth_bridge_proxy_address, "eth_bridge_proxy_address : init_governance_proxy", ) - .await; + .await + .context("Initializing governance proxy")?; sleep(Duration::from_secs(10)).await; - let eth_bridge = - StarknetLegacyEthBridge::deploy(self.core_contract.client().clone(), self.arg_config.dev).await; + let eth_bridge = StarknetLegacyEthBridge::deploy(self.core_contract.client().clone(), self.arg_config.dev) + .await + .context("Declaring starknet legacy eth bridge contract")?; log::info!("✴ī¸ ETH Bridge L1 deployment completed [Eth Bridge Address (L1) : {:?}]", eth_bridge.address()); - save_to_json("ETH_l1_bridge_address", &JsonValueType::EthAddress(eth_bridge.address())).unwrap(); + save_to_json("ETH_l1_bridge_address", &JsonValueType::EthAddress(eth_bridge.address())) + .context("Saving l1 eth bridge address to json")?; let account = build_single_owner_account( self.clients.provider_l2(), &self.arg_config.rollup_priv_key, - &convert_to_hex(&self.account_address.to_string()), + &convert_to_hex(&self.account_address.to_string())?, false, ) - .await; + .await + .context("Building single owner account")?; let l2_bridge_address = StarknetLegacyEthBridge::deploy_l2_contracts( self.clients.provider_l2(), @@ -149,10 +164,12 @@ impl<'a> EthBridge<'a> { eth_bridge_proxy_address, &account, ) - .await; + .await + .context("Deploying starknet legacy eth bridge l2 contracts")?; log::info!("✴ī¸ ETH Bridge L2 deployment completed [Eth Bridge Address (L2) : {:?}]", l2_bridge_address); - save_to_json("ETH_l2_bridge_address", &JsonValueType::StringType(l2_bridge_address.to_string())).unwrap(); + save_to_json("ETH_l2_bridge_address", &JsonValueType::StringType(l2_bridge_address.to_string())) + .context("Saving ETH l2 bridge address to json")?; let eth_address = deploy_eth_token_on_l2( self.clients.provider_l2(), @@ -161,19 +178,29 @@ impl<'a> EthBridge<'a> { &account, l2_bridge_address, ) - .await; + .await + .context("Deploying eth token on l2")?; log::info!("✴ī¸ L2 ETH token deployment successful."); // save_to_json("l2_eth_address", &JsonValueType::StringType(eth_address.to_string()))?; if self.arg_config.dev { - eth_bridge.initialize(self.core_contract.address()).await; + eth_bridge + .initialize(self.core_contract.address()) + .await + .context("Initializing eth bridge in dev mode on L1")?; } else { - eth_bridge.add_implementation_eth_bridge(self.core_contract.address()).await; - eth_bridge.upgrade_to_eth_bridge(self.core_contract.address()).await; + eth_bridge + .add_implementation_eth_bridge(self.core_contract.address()) + .await + .context("Adding implementation eth bridge on L1")?; + eth_bridge + .upgrade_to_eth_bridge(self.core_contract.address()) + .await + .context("Upgrading eth bridge on L1")?; } log::info!("✴ī¸ ETH Bridge initialization on L1 completed"); - sleep(Duration::from_secs(self.arg_config.l1_wait_time.parse().unwrap())).await; + sleep(Duration::from_secs(self.arg_config.l1_wait_time.parse().context("Parsing L1 wait time")?)).await; eth_bridge .setup_l2_bridge( @@ -183,7 +210,8 @@ impl<'a> EthBridge<'a> { &self.arg_config.rollup_priv_key, &account, ) - .await; + .await + .context("Setting up ETH bridge on L2")?; log::info!("✴ī¸ ETH Bridge initialization and setup on L2 completed"); eth_bridge @@ -191,13 +219,15 @@ impl<'a> EthBridge<'a> { "10000000000000000000000000000000000000000", "10000000000000000000000000000000000000000", l2_bridge_address, - Address::from_str(&self.arg_config.l1_multisig_address.to_string()).unwrap(), + Address::from_str(&self.arg_config.l1_multisig_address.to_string()) + .context("Parsing L1 multisig address")?, self.arg_config.dev, ) - .await; + .await + .context("Setting up ETH bridge on L1")?; log::info!("✴ī¸ ETH Bridge setup on L1 completed"); - EthBridgeSetupOutput { + Ok(EthBridgeSetupOutput { legacy_proxy_class_hash, starkgate_proxy_class_hash, erc20_legacy_class_hash, @@ -205,7 +235,7 @@ impl<'a> EthBridge<'a> { eth_proxy_address, eth_bridge_proxy_address, eth_bridge, - } + }) } } @@ -215,7 +245,7 @@ pub async fn deploy_eth_token_on_l2( eth_erc20_class_hash: FieldElement, account: &RpcAccount<'_>, eth_legacy_bridge_address: FieldElement, -) -> FieldElement { +) -> anyhow::Result { let deploy_tx = account .invoke_contract( account.address(), @@ -223,11 +253,16 @@ pub async fn deploy_eth_token_on_l2( vec![eth_erc20_class_hash, FieldElement::ZERO, FieldElement::ZERO, FieldElement::ZERO], None, ) + .context("Making deploy_contract transaction")? .send() .await - .expect("Error deploying the contract proxy."); - wait_for_transaction(rpc_provider_l2, deploy_tx.transaction_hash, "deploy_eth_token_on_l2 : deploy").await.unwrap(); - let contract_address = get_contract_address_from_deploy_tx(account.provider(), &deploy_tx).await.unwrap(); + .context("Error deploying the contract proxy")?; + wait_for_transaction(rpc_provider_l2, deploy_tx.transaction_hash, "deploy_eth_token_on_l2 : deploy") + .await + .context("Waiting for deploy_contract transaction to settle")?; + let contract_address = get_contract_address_from_deploy_tx(account.provider(), &deploy_tx) + .await + .context("Getting resulting contract address")?; log::debug!("Contract address (eth erc20) : {:?}", contract_address); @@ -238,15 +273,16 @@ pub async fn deploy_eth_token_on_l2( contract_address, FieldElement::ZERO, FieldElement::from(4u64), - FieldElement::from_byte_slice_be("Ether".as_bytes()).unwrap(), - FieldElement::from_byte_slice_be("ETH".as_bytes()).unwrap(), - FieldElement::from_str("18").unwrap(), + FieldElement::from_byte_slice_be("Ether".as_bytes()).expect("Parsing a constant"), + FieldElement::from_byte_slice_be("ETH".as_bytes()).expect("Parsing a constant"), + FieldElement::from_str("18").expect("Parsing a constant"), eth_legacy_bridge_address, FieldElement::ZERO, ], account, ) - .await; + .await + .context("Invoking add_implementation")?; wait_for_transaction( rpc_provider_l2, @@ -254,7 +290,7 @@ pub async fn deploy_eth_token_on_l2( "deploy_eth_token_on_l2 : add_implementation", ) .await - .unwrap(); + .context("Waiting for add_implementation transaction to settle")?; let upgrade_to_txn = invoke_contract( eth_proxy_address, @@ -263,18 +299,19 @@ pub async fn deploy_eth_token_on_l2( contract_address, FieldElement::ZERO, FieldElement::from(4u64), - FieldElement::from_byte_slice_be("Ether".as_bytes()).unwrap(), - FieldElement::from_byte_slice_be("ETH".as_bytes()).unwrap(), - FieldElement::from_str("18").unwrap(), + FieldElement::from_byte_slice_be("Ether".as_bytes()).expect("Parsing a constant"), + FieldElement::from_byte_slice_be("ETH".as_bytes()).expect("Parsing a constant"), + FieldElement::from_str("18").expect("Parsing a constant"), eth_legacy_bridge_address, FieldElement::ZERO, ], account, ) - .await; + .await + .context("Invoking upgrade_to")?; wait_for_transaction(rpc_provider_l2, upgrade_to_txn.transaction_hash, "deploy_eth_token_on_l2 : upgrade_to") .await - .unwrap(); - eth_proxy_address + .context("Waiting for upgrade_to transaction to settle")?; + Ok(eth_proxy_address) } diff --git a/src/setup_scripts/udc.rs b/src/setup_scripts/udc.rs index fefa296..4b04af4 100644 --- a/src/setup_scripts/udc.rs +++ b/src/setup_scripts/udc.rs @@ -1,5 +1,6 @@ use std::time::Duration; +use anyhow::Context; use starknet_accounts::ConnectedAccount; use starknet_ff::FieldElement; use tokio::time::sleep; @@ -26,14 +27,16 @@ impl<'a> UdcSetup<'a> { Self { account, account_address, arg_config } } - pub async fn setup(&self) -> UdcSetupOutput { + pub async fn setup(&self) -> anyhow::Result { let udc_class_hash = declare_contract(DeclarationInput::LegacyDeclarationInputs( String::from(UDC_PATH), self.arg_config.rollup_seq_url.clone(), )) - .await; + .await + .context("Declaring UDC class")?; log::debug!("đŸ“Ŗ UDC Class Hash Declared."); - save_to_json("udc_class_hash", &JsonValueType::StringType(udc_class_hash.to_string())).unwrap(); + save_to_json("udc_class_hash", &JsonValueType::StringType(udc_class_hash.to_string())) + .context("Saving udc class hash to json")?; sleep(Duration::from_secs(10)).await; let txn = self @@ -44,20 +47,24 @@ impl<'a> UdcSetup<'a> { Vec::from([udc_class_hash, FieldElement::ZERO, FieldElement::ONE, FieldElement::ZERO]), None, ) + .context("Making deploy_contract transaction")? .send() .await - .unwrap(); + .context("Sending deploy_contract transaction")?; wait_for_transaction( self.account.provider(), txn.transaction_hash, "deploy_non_bridge_contracts : deploy_contract : udc", ) .await - .unwrap(); - let udc_address = get_contract_address_from_deploy_tx(self.account.provider(), &txn).await.unwrap(); - save_to_json("udc_address", &JsonValueType::StringType(udc_address.to_string())).unwrap(); + .context("Waiting for deploy_contract transaction to settle")?; + let udc_address = get_contract_address_from_deploy_tx(self.account.provider(), &txn) + .await + .context("Getting resulting contract address")?; + save_to_json("udc_address", &JsonValueType::StringType(udc_address.to_string())) + .context("Saving UDC address to json")?; log::debug!("đŸ“Ŗ udc_address : {:?}", udc_address); - UdcSetupOutput { udc_class_hash, udc_address } + Ok(UdcSetupOutput { udc_class_hash, udc_address }) } } diff --git a/src/tests/erc20_bridge.rs b/src/tests/erc20_bridge.rs index 3a30a4e..e1ca691 100644 --- a/src/tests/erc20_bridge.rs +++ b/src/tests/erc20_bridge.rs @@ -20,7 +20,7 @@ pub async fn erc20_bridge_test_helper( token_bridge: StarknetTokenBridge, _l2_bridge_address: FieldElement, ) -> Result<(), anyhow::Error> { - token_bridge.approve(token_bridge.bridge_address(), 100000000.into()).await; + token_bridge.approve(token_bridge.bridge_address(), 100000000.into()).await.unwrap(); sleep(Duration::from_secs(arg_config.l1_wait_time.parse().unwrap())).await; log::debug!("Approval done [✅]"); log::debug!("Waiting for message to be consumed on l2 [âŗ]"); @@ -31,7 +31,8 @@ pub async fn erc20_bridge_test_helper( l2_erc20_token_address, FieldElement::from_str(L2_DEPLOYER_ADDRESS).unwrap(), ) - .await; + .await + .unwrap(); token_bridge .deposit( @@ -40,7 +41,8 @@ pub async fn erc20_bridge_test_helper( U256::from_str(L2_DEPLOYER_ADDRESS).unwrap(), U256::from_dec_str("100000000000000").unwrap(), ) - .await; + .await + .unwrap(); sleep(Duration::from_secs(arg_config.l1_wait_time.parse().unwrap())).await; log::debug!("Deposit done [💰]"); log::debug!("Waiting for message to be consumed on l2 [âŗ]"); @@ -51,7 +53,8 @@ pub async fn erc20_bridge_test_helper( l2_erc20_token_address, FieldElement::from_str(L2_DEPLOYER_ADDRESS).unwrap(), ) - .await; + .await + .unwrap(); assert_eq!(balance_before[0] + FieldElement::from_dec_str("10").unwrap(), balance_after[0]); diff --git a/src/tests/eth_bridge.rs b/src/tests/eth_bridge.rs index 8bf0c61..523dcd2 100644 --- a/src/tests/eth_bridge.rs +++ b/src/tests/eth_bridge.rs @@ -25,9 +25,10 @@ pub async fn eth_bridge_test_helper( l2_eth_address, FieldElement::from_hex_be(L2_DEPLOYER_ADDRESS).unwrap(), ) - .await; + .await + .unwrap(); - eth_bridge.deposit(10.into(), U256::from_str(L2_DEPLOYER_ADDRESS).unwrap(), 1000.into()).await; + eth_bridge.deposit(10.into(), U256::from_str(L2_DEPLOYER_ADDRESS).unwrap(), 1000.into()).await.unwrap(); log::debug!("ETH deposited on l1 [💰]"); sleep(Duration::from_secs(arg_config.cross_chain_wait_time)).await; sleep(Duration::from_secs((arg_config.l1_wait_time).parse()?)).await; @@ -38,7 +39,8 @@ pub async fn eth_bridge_test_helper( l2_eth_address, FieldElement::from_hex_be(L2_DEPLOYER_ADDRESS).unwrap(), ) - .await; + .await + .unwrap(); assert_eq!(balance_before[0] + FieldElement::from_dec_str("10").unwrap(), balance_after[0]); diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 15726a1..25f987b 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,3 +1,5 @@ +#![allow(clippy::unwrap_used)] + pub mod constants; mod erc20_bridge; mod eth_bridge; @@ -18,9 +20,9 @@ use crate::{bootstrap, CliArgs}; #[tokio::test] #[ignore] async fn deploy_bridge() -> Result<(), anyhow::Error> { - env_logger::init(); + let _ = env_logger::builder().is_test(true).try_init(); - bootstrap(&get_config()).await; + bootstrap(&get_config()).await.unwrap(); Ok(()) } @@ -29,9 +31,9 @@ async fn deploy_bridge() -> Result<(), anyhow::Error> { #[tokio::test] #[ignore] async fn deposit_and_withdraw_eth_bridge() -> Result<(), anyhow::Error> { - env_logger::init(); - let clients = Config::init(&get_config()).await; - let out = bootstrap(&get_config()).await; + let _ = env_logger::builder().is_test(true).try_init(); + let clients = Config::init(&get_config()).await.unwrap(); + let out = bootstrap(&get_config()).await.unwrap(); let _ = eth_bridge_test_helper( &clients, @@ -49,9 +51,9 @@ async fn deposit_and_withdraw_eth_bridge() -> Result<(), anyhow::Error> { #[tokio::test] #[ignore] async fn deposit_and_withdraw_erc20_bridge() -> Result<(), anyhow::Error> { - env_logger::init(); - let clients = Config::init(&get_config()).await; - let out = bootstrap(&get_config()).await; + let _ = env_logger::builder().is_test(true).try_init(); + let clients = Config::init(&get_config()).await.unwrap(); + let out = bootstrap(&get_config()).await.unwrap(); let _ = erc20_bridge_test_helper( &clients, @@ -68,9 +70,9 @@ async fn deposit_and_withdraw_erc20_bridge() -> Result<(), anyhow::Error> { #[rstest] #[tokio::test] async fn deposit_tests_both_bridges() -> Result<(), anyhow::Error> { - env_logger::init(); - let clients = Config::init(&get_config()).await; - let out = bootstrap(&get_config()).await; + let _ = env_logger::builder().is_test(true).try_init(); + let clients = Config::init(&get_config()).await.unwrap(); + let out = bootstrap(&get_config()).await.unwrap(); let _ = eth_bridge_test_helper( &clients, diff --git a/src/utils/mod.rs b/src/utils/mod.rs index fad9ced..b514def 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,7 +1,9 @@ +use std::fs; +use std::fs::File; use std::path::Path; use std::time::Duration; -use std::{fs, io}; +use anyhow::Context; use ethers::addressbook::Address; use ethers::types::U256; use num_bigint::BigUint; @@ -26,13 +28,19 @@ pub async fn invoke_contract<'a>( method: &str, calldata: Vec, account: &RpcAccount<'a>, -) -> InvokeTransactionResult { - let txn_res = - account.invoke_contract(contract, method, calldata, None).send().await.expect("Error in invoking the contract"); - - wait_for_transaction(account.provider(), txn_res.transaction_hash, "invoking_contract").await.unwrap(); - - txn_res +) -> anyhow::Result { + let txn_res = account + .invoke_contract(contract, method, calldata, None) + .with_context(|| format!("Making invoke transaction for contract {contract:#x}, method {method}"))? + .send() + .await + .with_context(|| format!("Sending invoke transaction for contract {contract:#x}, method {method}"))?; + + wait_for_transaction(account.provider(), txn_res.transaction_hash, "invoking_contract").await.with_context( + || format!("Waiting for invoke transaction for contract {contract:#x}, method {method} to settle"), + )?; + + Ok(txn_res) } pub fn pad_bytes(address: Address) -> Vec { @@ -47,12 +55,12 @@ pub async fn wait_for_transaction( provider_l2: &JsonRpcClient, transaction_hash: FieldElement, tag: &str, -) -> Result<(), anyhow::Error> { - let transaction_receipt = get_transaction_receipt(provider_l2, transaction_hash).await; +) -> anyhow::Result<()> { + let transaction_receipt = get_transaction_receipt(provider_l2, transaction_hash) + .await + .with_context(|| format!("Getting transaction receipt for transaction hash {transaction_hash:#x}"))?; - let transaction_status = transaction_receipt.ok().unwrap(); - - match transaction_status { + match transaction_receipt { Receipt(transaction_receipt) => { log::trace!("txn : {:?} : {:?}", tag, transaction_receipt); Ok(()) @@ -74,12 +82,15 @@ pub enum JsonValueType { StringType(String), } -pub fn save_to_json(key: &str, value: &JsonValueType) -> Result<(), io::Error> { - let file_path: &str = "./data/addresses.json"; - let data = fs::read_to_string(file_path); - let mut json: Map = match data { - Ok(content) => serde_json::from_str(&content).unwrap_or_else(|_| Map::new()), - Err(_) => Map::new(), +pub fn save_to_json(key: &str, value: &JsonValueType) -> anyhow::Result<()> { + let file_path = Path::new("./data/addresses.json"); + + let mut json: Map = if file_path.exists() { + let file = File::open(file_path).with_context(|| format!("Opening file {}", file_path.display()))?; + serde_json::from_reader(file) + .with_context(|| format!("Reading and deserializing file {}", file_path.display()))? + } else { + Default::default() }; match value { @@ -87,24 +98,25 @@ pub fn save_to_json(key: &str, value: &JsonValueType) -> Result<(), io::Error> { json.insert(key.to_string(), serde_json::json!(x)); } JsonValueType::StringType(x) => { - json.insert(key.to_string(), serde_json::json!(convert_to_hex(x))); + json.insert(key.to_string(), serde_json::json!(convert_to_hex(x)?)); } } - let updated_json = serde_json::to_string_pretty(&json)?; + let updated_json = serde_json::to_string_pretty(&json).context("Serializing to json")?; // Ensure the directory exists before writing the file if let Some(dir_path) = Path::new(file_path).parent() { - fs::create_dir_all(dir_path)?; + fs::create_dir_all(dir_path) + .with_context(|| format!("Creating the parent directories for file {}", file_path.display()))?; } - fs::write(file_path, updated_json)?; + fs::write(file_path, updated_json).with_context(|| format!("Writing the json file to {}", file_path.display()))?; Ok(()) } -pub fn convert_to_hex(address: &str) -> String { - let big_uint = address.parse::().map_err(|_| "Invalid number"); - let hex = big_uint.expect("error converting decimal string ---> hex string").to_str_radix(16); - "0x".to_string() + &hex +pub fn convert_to_hex(address: &str) -> anyhow::Result { + let big_uint = address.parse::().with_context(|| format!("Parsing address {address}"))?; + let hex = big_uint.to_str_radix(16); + Ok(format!("0x{hex}")) }