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
- Read the attached provider error — it names the real cause (insufficient funds, gas price, nonce, estimation revert)
- Fund the deployer address and retry
- If nonce-related, let in-flight txs settle or resync the account nonce on the node
- 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
- Fund deployer accounts as an explicit setup step with a balance assertion
- Don't share the deployer key across processes that advance its nonce
- Set gas parameters appropriate for the target network before deploying
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
- get_logs RPC error: {}
- timed out after {:?}
- NOT IMPLEMENTED
- Connector ${evmWalletId} not found
- Missing result field
AI-assisted analysis of unionlabs/union@031785bb6d (2026-08-16).
Data as JSON: /api/errors/49ef3bb30d27abd1.
Report an issue: GitHub.