Repository navigation
Conversation
Rigidity
marked this pull request as ready for review
November 15, 2026 03:52
This was referenced Nov 15, 2026
A vault wallet's coins are spent by messages from the vault singleton, which is recreated with the same custody. Change goes to the vault's first address, the fee is reserved from its XCH, and a second spend is refused while one is pending, since the custody signs over the vault's coin id. Clear signing parses what a vault spend does before it's signed.
… summarize vault spends The mint stores the custody and recovery keys and adds the vault wallet before it's submitted, and removes them if submitting fails. A mint that's never found on chain is removed once its transaction expires. Vault transactions are signed with the keychain keys of the vault's signers, after clear signing checks that they don't change its custody.
… signers The vault wallet is opened right after a mint, which stops the funding wallet from submitting it later.
…show clear signing in confirmations BLS wallets get a Mint Vault page that walks through the custody key, a new recovery phrase, the recovery timelock and the fee. Vault wallets with a custody signer can use the send, multi-send and coin management flows, and the confirmation dialog shows what clear signing found, refusing spends that change the vault's custody.
Rigidity
force-pushed
the
rigidity/vault-spend
branch
from
November 15, 2026 06:00
b820466 to
6304201
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #952. Closes #951. Part of #945. Stacked on #969.
Vault wallets can now spend, and BLS wallets can mint vaults. This is the base of the rest of Phase 2: recovery (#954), assets (#953), offers (#955) and the external surface (#956) build on this pipeline.
Spending from a vault
Wallet::complete_spendshas a vault path (wallet/vault.rs). Each coin at a vault address outputs the conditions the action system chose through a delegated spend, and the vault approves it withSEND_MESSAGE(0x17, delegated_puzzle_hash, [coin_id]). P2 singleton coins useP2Singleton::spend, andP2ConditionsOrSingletoncoins their singleton path. The vault is recreated with the same custody and amount, withASSERT_MY_AMOUNT. It's ported from the SDK's test-onlyTestVault::partial_custom_spend.Relation::Nonerather than theAssertConcurrentring. Conditions no coin can output, such as the fee of a transaction that only spends the vault, are output by the vault (return_unplaced_conditions) instead of an intermediate coin.custody_mips_spend) goes down the custody side of the top level 1 of 2, or the top level of a vault without recovery. For an M of N, members whose keys are attached are chosen first. Only non-fast-forward BLS members can be spent so far; others returnVaultMemberNotSupported(Support secp256r1 secure element signers for vaults #957).vault_coins, or its launcher (coin_by_id) for the eve coin.Signing
sign_with_secret_keyssigns eachAGG_SIG_MEwhose key is one of the vault's signers, with the sameAGG_SIG_UNSAFErefusal as BLS wallets.Sage::sign_withpicks this for vault wallets, using the signers inwallets.toml. A vault with no signers fails withNoVaultSigners.parse_vault_spend_bundlefinds the vault's spend and runs the SDK'sparse_vault_transactionon it. A spend that can't be verified, changes the vault's custody, or melts it isn't signed. Nothing in this PR does either on purpose; rekeying and recovery (Recover, cancel, and rekey vaults #954) will say so explicitly.TransactionSummary.vault(VaultSpendRecord) shows the result: verified, custody changed, melted, the reserved fee, and XCH that leaves without being accounted for. The confirmation dialog shows it and disables Submit when Sage won't sign.Hardened by #1018. In this PR,
sign_withsigns any spend that doesn't include the vault coin. So a dApp'ssign_coin_spendscould ask the custody key for anAGG_SIG_MEon another coin, or include the vault coin twice. #1018 restricts vault keys to signingAGG_SIG_MEonly for the single vault spend that clear signing parsed. The stack merges together, so this is never released on its own.One spend at a time
Decision: the custody signs over the vault's coin id, so a second spend while one is pending would spend the same coin.
spendable_vaultrefuses withVaultSpendPendinguntil the pending one confirms, and it's checked before coin selection, so the error explains why. Chaining from the pending child is left for fast-forward members (#957). A vault with a pending recovery can't be spent here either (#954 adds cancel).Minting (#951)
mint_vault { name, emoji, custody_fingerprint?, recovery_mnemonic | recovery_fingerprint, recovery_timelock, fee, auto_submit }, funded by the open BLS wallet. The vault usesForceSingletonRecreation, nonce 0 and non-fast-forward BLS members, with the CHIP-0043 memo in the launcher solution, so it imports from its id alone.m/12381'/8444'/12'/i'(purpose 12 doesn't overlap Chia's 0 to 6), using the first index no vault wallet uses, so the wallet's mnemonic backs it up and each vault gets its own key. An existing keychain key can be chosen instead.auto_submitnothing is stored. With it, the keys and the wallet entry are saved first, then the mint is sent to the peers right away (the vault wallet opens next, which stops the funding wallet from submitting later) and recorded as the funding wallet's pending transaction. If it's rejected, the wallet and the keys it added are removed.wallets.tomlkeepsvault_mint { funding_fingerprint, transaction_id, expires_at }until the vault is found.get_vaultandlogincheck it: found on chain clears it, and a mint whose transaction expired (plus 10 minutes) without the launcher appearing is removed.get_vaultreturnsminting.UI
Testing
wallet/vault/tests.rs): minting with both versions (the memo decodes back to the config), minting with a fee, XCH sends with change to the vault for both versions, a fee-only spend, CAT sends, multi-send of XCH and CATs, split and combine, the pending-spend refusal, the singleton not being selectable, a wrong signer failing to sign, clear signing flagging a custody change, and coins received while the vault wallet was closed.tests/vault_mint.rs): preview without storing anything, minting, the signers and a distinct second custody key, opening the vault, receiving and sending XCH with the clear-signing summary; rejected timelocks and key choices storing nothing; a pending mint kept and shown as minting, and an expired one removed.cargo clippy --workspace --all-features --all-targets -D warnings,cargo fmt,cargo test --workspace,cargo machete,cargo sqlx prepare --workspace --check,pnpm prettier:check,pnpm lint,tsc -bandpnpm test:unit(169) pass.src/bindings.tswas regenerated withpnpm generate:bindings; two queries were added to.sqlx.Notes
EverythingWithSingletonTail(the vault's extra-delta message), NFTs, DIDs, options and clawbacks (Manage NFTs, DIDs, options, CAT issuance, and clawbacks from a vault #953); offers (Make, take, and cancel offers from a vault #955); recovery and rekey (Recover, cancel, and rekey vaults #954); WalletConnect and Sage Apps (Support vault and watch wallets across RPC, WalletConnect, and Sage Apps #956). Coins locked to a fixed set of conditions are still selectable like other vault coins until Make, take, and cancel offers from a vault #955 tracks them as locked balance.pnpm extractfor the new strings.