To fully exit an asset today, a user must first read their available balance (get_available) off-chain and then submit a withdraw for that exact amount. This is a race: if a task completes between the read and the withdraw, the available balance changes and the user either leaves dust behind or over-requests and reverts with InsufficientAvailable.
Add a withdraw_all that computes the withdrawable amount on-chain and transfers exactly the unlocked portion in a single call:
pub fn withdraw_all(env: Env, user: Address, asset: Address) -> Result<i128, VaultError> {
user.require_auth();
let asset_key = DataKey::UserAsset(user.clone(), asset.clone());
let account: UserAssetAccount = env.storage().persistent()
.get(&asset_key).ok_or(VaultError::InsufficientBalance)?;
let available = account.balance - account.locked;
if available <= 0 {
return Err(VaultError::InsufficientAvailable);
}
Self::withdraw(env, user, asset, available)?; // reuse existing logic + event
Ok(available)
}
Reusing withdraw means the existing per-asset locked guard, the WithdrawEvent, TTL bumps, and logging all apply unchanged — this function is purely a safe "max" wrapper. It returns the amount withdrawn so the caller can display it.
Acceptance criteria
Relevant files
contracts/agent-vault/src/lib.rs (new function reusing withdraw)
contracts/agent-vault/src/tests.rs
Notes for contributors
If you'd like to work on this, comment below so we can assign it to you.
Questions welcome — see CONTRIBUTING.md for setup
and workflow.
To fully exit an asset today, a user must first read their available balance (
get_available) off-chain and then submit awithdrawfor that exact amount. This is a race: if a task completes between the read and the withdraw, the available balance changes and the user either leaves dust behind or over-requests and reverts withInsufficientAvailable.Add a
withdraw_allthat computes the withdrawable amount on-chain and transfers exactly the unlocked portion in a single call:Reusing
withdrawmeans the existing per-assetlockedguard, theWithdrawEvent, TTL bumps, and logging all apply unchanged — this function is purely a safe "max" wrapper. It returns the amount withdrawn so the caller can display it.Acceptance criteria
withdraw_all(env, user, asset) -> Result<i128, VaultError>withdraws exactlybalance - lockedfor that assetInsufficientAvailablewhen nothing is withdrawable (all locked or zero balance)WithdrawEvent(via reuse ofwithdraw)cargo testandcargo clippy --all-targets -- -D warningspassRelevant files
contracts/agent-vault/src/lib.rs(new function reusingwithdraw)contracts/agent-vault/src/tests.rsNotes for contributors
If you'd like to work on this, comment below so we can assign it to you.
Questions welcome — see CONTRIBUTING.md for setup
and workflow.