diff --git a/contracts/finance/sources/vesting_wallet.move b/contracts/finance/sources/vesting_wallet.move index e1c50841..b4f8f3aa 100644 --- a/contracts/finance/sources/vesting_wallet.move +++ b/contracts/finance/sources/vesting_wallet.move @@ -554,12 +554,19 @@ public fun release( /// /// Coins `public_transfer`'d to this wallet's address but not claimed before this call /// are invisible to the held-balance check, and funds settled at the wallet's address -/// must be swept before teardown. Funds sent to this address after the wallet's `UID` -/// is deleted have no path back: a coin `public_transfer`'d there is unaddressable, and -/// a balance `send_funds`'d there settles against an object whose `UID` is gone, so no -/// `withdraw_funds_from_object` path can ever reclaim it. Pair destruction with halting -/// upstream emissions, claiming outstanding transferred coins, sweeping settled funds, -/// and letting the halt settle before tearing the wallet down. +/// must be swept before teardown. Anyone can settle additional `C` to the wallet's +/// address; once those funds appear in `root`, `destroy_empty` aborts with +/// `EUnsweptFunds`, so teardown should be treated as retryable: sweep the newly settled +/// funds, then retry. +/// +/// The `root` snapshot cannot detect funds sent in the same checkpoint as teardown. +/// If teardown deletes the wallet `UID` before those in-flight funds are visible, they +/// can later settle to an address no `withdraw_funds_from_object` path can reclaim. +/// Funds sent to this address after the wallet's `UID` is deleted have the same problem: +/// a coin `public_transfer`'d there is unaddressable, and a balance `send_funds`'d there +/// settles against an object whose `UID` is gone. Pair destruction with halting upstream +/// emissions, claiming outstanding transferred coins, sweeping settled funds, and letting +/// at least one full checkpoint elapse before tearing the wallet down. /// /// #### Parameters /// - `wallet`: The wallet to destroy. Must hold a zero balance and have no pending diff --git a/contracts/finance/sources/vesting_wallet_linear.move b/contracts/finance/sources/vesting_wallet_linear.move index a44e2944..f37954f5 100644 --- a/contracts/finance/sources/vesting_wallet_linear.move +++ b/contracts/finance/sources/vesting_wallet_linear.move @@ -373,12 +373,17 @@ public fun releasable(wallet: &VestingWallet, clock: &Cloc /// The cap holder bears the strand risk the old beneficiary gate guarded. A coin /// `public_transfer`'d to the wallet's address but not yet `receive_and_deposit`'d is /// invisible to the held-balance check, and settled address-balance funds must be swept -/// before `destroy_empty` accepts the teardown. Route the cap to the party that should -/// own that decision (commonly the beneficiary or its controller). Destruction is an -/// operational step rather than a guaranteed one-shot: halt upstream emissions first, -/// claim any transferred coins that already target the wallet, sweep settled funds, -/// and allow at least one checkpoint for in-flight emissions to settle before retrying -/// teardown. +/// before `destroy_empty` accepts the teardown. Newly settled third-party funds can make +/// `destroy_empty` abort with `EUnsweptFunds`, so teardown should be treated as +/// retryable: sweep and retry. +/// +/// Route the cap to the party that should own that decision (commonly the beneficiary or +/// its controller). Destruction is an operational step rather than a guaranteed one-shot: +/// halt upstream emissions first, claim any transferred coins that already target the +/// wallet, sweep settled funds, and allow at least one full checkpoint for in-flight +/// emissions to become visible before retrying teardown. Funds sent in the same +/// checkpoint as teardown may be invisible to `destroy_empty`; if the wallet `UID` is +/// deleted before they settle, they can be permanently stranded. /// /// The ended gate is retained: it stops a wallet being torn down ahead of a scheduled /// future deposit, front-running funding intended to arrive later. It cannot detect