From 30c78cbfbd4cbb1b271fbe26178f9c3295df00cb Mon Sep 17 00:00:00 2001 From: Jimmy Claw Date: Mon, 2 Mar 2026 20:32:38 +0000 Subject: [PATCH 1/2] fix(wallet): await on-chain confirmation before returning from deploy-program The DeployProgram command previously submitted the transaction and immediately returned success, even if the sequencer rejected it. Now it polls get_transaction_by_hash until the transaction appears in a block, exiting non-zero with a clear error message after a 30s timeout. Co-Authored-By: Claude Opus 4.6 --- wallet/src/cli/mod.rs | 51 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/wallet/src/cli/mod.rs b/wallet/src/cli/mod.rs index 87c2bb31..777ed840 100644 --- a/wallet/src/cli/mod.rs +++ b/wallet/src/cli/mod.rs @@ -97,6 +97,32 @@ pub enum SubcommandReturnValue { SyncedToBlock(u64), } +/// Poll the sequencer until a transaction appears in a block. +/// Returns `Ok(true)` if confirmed, `Ok(false)` if timed out. +async fn wait_for_tx_confirmation( + client: &common::sequencer_client::SequencerClient, + tx_hash: HashType, + max_attempts: u32, + poll_interval_ms: u64, +) -> Result { + for attempt in 1..=max_attempts { + tokio::time::sleep(std::time::Duration::from_millis(poll_interval_ms)).await; + + match client.get_transaction_by_hash(tx_hash).await { + Ok(resp) if resp.transaction.is_some() => { + return Ok(true); + } + Ok(_) => { + println!(" Attempt {}/{}: pending...", attempt, max_attempts); + } + Err(e) => { + println!(" Attempt {}/{}: error querying tx: {}", attempt, max_attempts, e); + } + } + } + Ok(false) +} + pub async fn execute_subcommand( wallet_core: &mut WalletCore, command: Command, @@ -169,12 +195,35 @@ pub async fn execute_subcommand( ))?; let message = nssa::program_deployment_transaction::Message::new(bytecode); let transaction = ProgramDeploymentTransaction::new(message); - let _response = wallet_core + + println!("Submitting program deployment transaction..."); + let response = wallet_core .sequencer_client .send_tx_program(transaction) .await .context("Transaction submission error")?; + let tx_hash = response.tx_hash; + println!("Transaction submitted: {}", tx_hash); + println!("Waiting for confirmation..."); + + // Poll until the transaction is included in a block + let confirmed = wait_for_tx_confirmation( + &wallet_core.sequencer_client, + tx_hash, + 30, // max attempts + 1000, // ms between polls + ) + .await?; + + if !confirmed { + anyhow::bail!( + "Transaction was not confirmed after timeout. \ + The program may not have been deployed. Check sequencer logs." + ); + } + + println!("Program deployed successfully! Program ID: {}", tx_hash); SubcommandReturnValue::Empty } }; From 45ae8063b1901353907bedeaf2ce7966b1a13af2 Mon Sep 17 00:00:00 2001 From: Jimmy Claw Date: Mon, 2 Mar 2026 20:58:31 +0000 Subject: [PATCH 2/2] =?UTF-8?q?fix(wallet):=20poll=20every=202s=20for=20up?= =?UTF-8?q?=20to=203=20blocks=20(45s)=20instead=20of=2030=20=C3=97=201s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Block time in lssa is 15s, so 3 blocks = 45s is the right timeout. Polling every 2s (23 attempts) is responsive without hammering the sequencer. --- wallet/src/cli/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wallet/src/cli/mod.rs b/wallet/src/cli/mod.rs index 777ed840..d635f790 100644 --- a/wallet/src/cli/mod.rs +++ b/wallet/src/cli/mod.rs @@ -211,15 +211,15 @@ pub async fn execute_subcommand( let confirmed = wait_for_tx_confirmation( &wallet_core.sequencer_client, tx_hash, - 30, // max attempts - 1000, // ms between polls + 23, // max attempts (3 blocks × 15s, polling every 2s) + 2000, // ms between polls (2s) ) .await?; if !confirmed { anyhow::bail!( "Transaction was not confirmed after timeout. \ - The program may not have been deployed. Check sequencer logs." + Timed out after ~3 blocks (45s). The program may not have been deployed. Check sequencer logs." ); }