unionlabs/union · error · anyhow::Error

Failed to deploy contract: {:?}

Error message

Failed to deploy contract: {:?}

What it means

`deploy_basic_erc20` broadcasts a raw deploy via `RawCallBuilder`; nonce-too-low errors are retried up to 5 times with 10s sleeps, and any other error — or a nonce error persisting past the retries — is reported as 'Failed to deploy contract' with the provider error attached via `{:?}`.

Source

Thrown at tools/union-test/src/evm.rs:708

            let pending = call.send().await;
            match pending {
                Ok(pending) => {
                    let tx_hash = *pending.tx_hash();
                    self.wait_for_tx_inclusion(&provider, tx_hash.into())
                        .await?;
                    let receipt = pending.get_receipt().await?;

                    let address = receipt
                        .contract_address
                        .expect("deploy didnt return an address");
                    return Ok(address.into());
                }
                Err(err) if attempts <= 5 && self.is_nonce_too_low(&err) => {
                    tokio::time::sleep(Duration::from_secs(10)).await;
                    continue;
                }
                Err(err) => {
                    return Err(anyhow::anyhow!("Failed to deploy contract: {:?}", err));
                }
            }
        }
    }

    pub async fn send_ibc_transaction(
        &self,
        msg: RawCallBuilder<DynProvider<AnyNetwork>, AnyNetwork>,
    ) -> RpcResult<(FixedBytes<32>, u64)> {
        let res = self
            .keyring
            .with({
                let msg = msg.clone();
                move |wallet| -> _ { AssertUnwindSafe(self.submit_transaction(wallet, msg)) }
            })
            .await;

        match res {

View on GitHub (pinned to 031785bb6d)

Solutions

  1. Read the attached provider error — it names the real cause (insufficient funds, gas price, nonce, estimation revert)
  2. Fund the deployer address and retry
  3. If nonce-related, let in-flight txs settle or resync the account nonce on the node
  4. Raise gas limits / max_gas_price if estimation or pricing is the cause

Example fix

// before: deploy immediately and hope
let erc20 = evm.deploy_basic_erc20(spender, provider.clone()).await?; 
// after: verify deployer funds first
let deployer = evm.keyring.first_address();
let bal = provider.get_balance(deployer).latest().await?;
anyhow::ensure!(bal > alloy::primitives::U256::from(10_000_000_000_000_000u128), "deployer underfunded");
let erc20 = evm.deploy_basic_erc20(spender, provider.clone()).await?;
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-flight: deployer is funded
let deployer = evm.keyring.keys.iter().next().unwrap().address;
let bal = provider.get_balance(deployer.into()).latest().await?;
anyhow::ensure!(!bal.is_zero(), "deployer {deployer} has no funds");

Try / catch

match evm.deploy_basic_erc20(spender, provider.clone()).await {
    Ok(addr) => Ok(addr),
    Err(e) => {
        let msg = format!("{e:#}");
        if msg.contains("insufficient funds") { /* fund and retry once */ }
        else { return Err(e); }
    }
}

Prevention

When it happens

Trigger: Gas estimation fails or the broadcast is rejected: deployer out of funds; gas price above the configured maximum; node rejecting the tx (mempool/nonce issues); invalid constructor args appended to the bytecode; nonce remaining too-low for >~50s because the account's on-node nonce is far ahead.

Common situations: Test wallets unfunded on the target chain; shared devnet where the same key is used elsewhere, advancing its nonce; max_gas_price too low during gas spikes; malformed constructor encoding.

Related errors


AI-assisted analysis of unionlabs/union@031785bb6d (2026-08-16). Data as JSON: /api/errors/49ef3bb30d27abd1. Report an issue: GitHub.