Summary
On the wallet-backed payment path, ant-core throws away a perfectly good typed error from evmlib and stringifies it into the generic Error::Payment(String), so downstream consumers (the mobile FFI / SDK, the CLI) can't distinguish "not enough ANT tokens" from any other payment failure without brittle substring matching.
Where the information is lost
evmlib already exposes a distinct variant and pre-checks the balance before submitting:
evmlib/src/wallet.rs:35 — Error::InsufficientTokensForQuotes(Amount, Amount) (have, need)
pay_for_quotes() and pay_for_merkle_tree() both pre-check balance_of_tokens() and return InsufficientTokensForQuotes(have, need) before sending any tx.
ant-core then flattens it:
ant-core/src/data/client/payment.rs:92-94 — .map_err(|e| Error::Payment(format!("On-chain payment failed: {e}")))
ant-core/src/data/client/payment.rs:126-129 — token approval, same pattern
ant-core/src/data/client/merkle.rs:488-495 — .map_err(|e| Error::Payment(format!("Merkle batch payment failed: {e}")))
Ask
- Add a typed variant to
ant-core/src/data/error.rs, e.g.:
#[error("insufficient funds: have {have} atto, need {need} atto")]
InsufficientFunds { have: String, need: String },
- In
payment.rs / merkle.rs, match evmlib::wallet::Error::InsufficientTokensForQuotes(have, need) (reached via ant_protocol::evm) and map it to the new variant before the generic stringify fallback.
This lets consumers surface a clean "insufficient funds — top up your wallet" instead of a generic payment error.
Out of scope / follow-ups (noted for context)
- ETH-for-gas shortfall is not pre-checked; it comes back as an RPC
"insufficient funds for gas", and evmlib/src/retry.rs:353 extract_revert_data() currently always returns None, so the revert reason is dropped. Distinguishing gas shortfall would need evmlib to populate revert data (separate, larger change).
- The external-signer path (mobile WalletConnect) never routes payment through ant-core, so this variant doesn't apply there — that path is handled SDK-side with a read-only pre-flight balance check.
Downstream
Consumed by the mobile SDK error mapping in WithAutonomi/ant-sdk (ffi/rust/ant-ffi/src/lib.rs From<ant_core::data::Error>), tracked on Linear V2-601. Once this lands, the FFI adds a ClientError::InsufficientFunds variant.
Summary
On the wallet-backed payment path, ant-core throws away a perfectly good typed error from evmlib and stringifies it into the generic
Error::Payment(String), so downstream consumers (the mobile FFI / SDK, the CLI) can't distinguish "not enough ANT tokens" from any other payment failure without brittle substring matching.Where the information is lost
evmlib already exposes a distinct variant and pre-checks the balance before submitting:
evmlib/src/wallet.rs:35—Error::InsufficientTokensForQuotes(Amount, Amount)(have, need)pay_for_quotes()andpay_for_merkle_tree()both pre-checkbalance_of_tokens()and returnInsufficientTokensForQuotes(have, need)before sending any tx.ant-core then flattens it:
ant-core/src/data/client/payment.rs:92-94—.map_err(|e| Error::Payment(format!("On-chain payment failed: {e}")))ant-core/src/data/client/payment.rs:126-129— token approval, same patternant-core/src/data/client/merkle.rs:488-495—.map_err(|e| Error::Payment(format!("Merkle batch payment failed: {e}")))Ask
ant-core/src/data/error.rs, e.g.:payment.rs/merkle.rs, matchevmlib::wallet::Error::InsufficientTokensForQuotes(have, need)(reached viaant_protocol::evm) and map it to the new variant before the generic stringify fallback.This lets consumers surface a clean "insufficient funds — top up your wallet" instead of a generic payment error.
Out of scope / follow-ups (noted for context)
"insufficient funds for gas", andevmlib/src/retry.rs:353extract_revert_data()currently always returnsNone, so the revert reason is dropped. Distinguishing gas shortfall would need evmlib to populate revert data (separate, larger change).Downstream
Consumed by the mobile SDK error mapping in WithAutonomi/ant-sdk (
ffi/rust/ant-ffi/src/lib.rsFrom<ant_core::data::Error>), tracked on Linear V2-601. Once this lands, the FFI adds aClientError::InsufficientFundsvariant.