datahaven-xyz/datahaven · critical
InsufficientSovereignBalance
InsufficientSovereignBalance
Error message
InsufficientSovereignBalance
What it means
unlock_tokens (the Ethereum→DataHaven direction) verifies that the Ethereum sovereign account holds enough free balance to cover the unlock. Available funds are computed as balance minus minimum_balance (existential deposit buffer); if the requested amount exceeds that, the pallet fails with InsufficientSovereignBalance so the sovereign account is never reaped below its existential deposit.
Solutions
- Fund the Ethereum sovereign account on DataHaven so its balance minus existential deposit covers pending unlocks.
- Check T::Currency::balance(sovereign) before submitting and queue the unlock until liquidity exists.
- Investigate inflow/outflow accounting if the sovereign is persistently short — it may indicate minted withdrawals exceeded locked deposits.
- Retry after the account is topped up; the user's funds were not moved.
Example fix
// before: submit blindly
await api.tx.datahavenNativeTransfer.unlockTokens(who, amount).signAndSend(caller);
// after
const sovereign = await api.query.datahavenNativeTransfer.ethereumSovereignAccount();
const { data: bal } = await api.query.system.account(sovereign);
const available = bal.free - api.consts.balances.existentialDeposit;
if (available < amount) throw new Error('sovereign balance insufficient for unlock');
await api.tx.datahavenNativeTransfer.unlockTokens(who, amount).signAndSend(caller); Defensive patterns
Strategy: validation
Validate before calling
const sovereign = await api.query.datahavenNativeTransfer.ethereumSovereignAccount();
const { data: bal } = await api.query.system.account(sovereign);
const available = bal.free.toBigInt() - api.consts.balances.existentialDeposit.toBigInt();
if (available < amount) throw new Error(`Sovereign account short by ${amount - available}`); Try / catch
try {
await api.tx.datahavenNativeTransfer.unlockTokens(who, amount).signAndSend(caller);
} catch (e) {
if (String(e).includes('InsufficientSovereignBalance')) alertOps('Sovereign underfunded; top up before retry');
else throw e;
} Prevention
- Monitor sovereign account balance with alerts against aggregate pending unlocks.
- Keep a funding runbook to top up the sovereign account quickly.
- Reconcile deposits (locks on Ethereum) with unlocks to detect insolvency early.
When it happens
Trigger: Calling unlock_tokens (lib.rs:302 region) where amount > Currency::balance(sovereign) - Currency::minimum_balance(), i.e. the sovereign account's spendable (post-buffer) balance is below the requested amount.
Common situations: Sovereign account underfunded because more tokens were bridged out of Ethereum than deposited in (bridge insolvency risk); several users unlocking after large withdrawals drained the account; misconfigured sovereign funding during testnet setup.
Related errors
AI-assisted analysis of datahaven-xyz/datahaven@edcb13dbbc (2026-09-13).
Data as JSON: /api/errors/349e6759e59da23c.
Report an issue: GitHub.
Appendix: source
Thrown at operator/pallets/datahaven-native-transfer/src/lib.rs:302
Self::deposit_event(Event::TokensLocked {
account: who.clone(),
amount,
});
Ok(())
}
/// Unlock tokens returning from Ethereum
///
/// Transfers tokens from the Ethereum sovereign account back to user
pub fn unlock_tokens(who: &T::AccountId, amount: BalanceOf<T>) -> DispatchResult {
let sovereign = T::EthereumSovereignAccount::get();
let balance = T::Currency::balance(&sovereign);
let minimum_balance = T::Currency::minimum_balance();
let available_balance = balance.saturating_sub(minimum_balance);
// Allow unlocking only from funds that exceed the existential buffer.
ensure!(
available_balance >= amount,
Error::<T>::InsufficientSovereignBalance
);
// Transfer from the Ethereum sovereign account
T::Currency::transfer(&sovereign, who, amount, Preservation::Preserve)?;
Self::deposit_event(Event::TokensUnlocked {
account: who.clone(),
amount,
});
Ok(())
}
/// Get the balance of locked tokens in the Ethereum sovereign account
/// This represents the total amount of tokens locked for transfers to Ethereum
pub fn total_locked_balance() -> BalanceOf<T> {View on GitHub (pinned to edcb13dbbc)