feat(signer): sign OpenZeppelin smart-account authorizations (no new flags) - #27
Merged
Merged
Conversation
…flags)
`stellar` could not sign an operation whose `require_auth()` resolves to a
smart-contract account (a C… address with a custom `__check_auth`): the signer
rejected contract-address credentials and skipped CAP-71 `AddressWithDelegates`.
So `stellar registry publish --author <smart-account>` was impossible with the
CLI alone.
This adds smart-account signing keyed only on (the auth entry's own contract
address) + (`--sign-with-key`) — no smart-account-specific flags. When the
signing loop hits a contract-address credential, `config` DISCOVERS the
authorizing OpenZeppelin context rule from the account's own on-chain views
(`get_context_rules_count` / `get_context_rule`, read over read-only
simulation), matching the rule by scope (the invocation's target contract) and
signer (the signing key), and derives the rule id + mode + verifier. The
RPC-free signer then builds the credential: OZ `AuthPayload { signers,
context_rule_ids }` over the rule-bound digest, delivered as a CAP-71
`AddressWithDelegates` for a Delegated signer, or an `Address` credential for an
External signer.
Because a smart account's `__check_auth` runs verifier + policy cross-calls that
recording-mode simulation does not execute, `sim_sign_and_send_tx` re-simulates
in enforce mode when a smart-account entry was signed, so the fee reflects the
true footprint.
Normal G-account flows are unaffected — discovery only runs when a
contract-address auth entry is present.
- new: cmd/soroban-cli/src/signer/smart_account.rs (signing + discovery)
- signer::sign_soroban_authorizations takes &[SmartAccountAuth], returns
SignedAuthTxn { tx, smart_account_signed }
- config discovers per contract-address entry and builds the delegate from
--sign-with-key; tx.rs gates the enforce re-sim on the flag
Refs #26
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R32ivDDvuLnRZrmzkvzoeu
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.
Implements #26 (a focused first cut).
What
stellarcan now sign an operation whoserequire_auth()resolves to an OpenZeppelin smart account (aC…address with a custom__check_auth) — sostellar registry publish --author <smart-account> --sign-with-key <delegate>works with the CLI alone, no bespoke signer binary.No smart-account-specific flags. Signing is keyed only on the auth entry's own contract address +
--sign-with-key.How
config, where the RPC client lives). When the tx has a contract-address auth credential, resolve the authorizing context rule from the account's own on-chain views —get_context_rules_count/get_context_ruleover read-onlysimulateTransaction— and match by scope (the invocation's target contract, orDefault) and signer (the--sign-with-keykey). That yields the rule id, the mode (Delegated/External), and the verifier — none are user flags. (Same read path perch'sperch-deployscan_rulesuses.)signer/smart_account.rs, RPC-free). Builds the OZAuthPayload { signers, context_rule_ids }over the rule-bound digest — delivered as a CAP-71AddressWithDelegatesfor aDelegatedsigner (the delegate signs theSorobanAuthorizationWithAddresspreimage), or anAddresscredential for anExternalsigner.tx.rs). A smart account's__check_authruns verifier + policy cross-calls that recording-mode simulation doesn't execute, sosim_sign_and_send_txre-simulates in enforce mode when a smart-account entry was signed, capturing the true footprint before the fee is set.The contract-address arm of
sign_soroban_authorizations(previously a hardMissingSignerForAddress) becomes this path;sign_soroban_authorizationsnow takes&[SmartAccountAuth]and returnsSignedAuthTxn { tx, smart_account_signed }.Scope / follow-ups (tracked in #26)
CreateContractscopes, and passkey/WebAuthnExternalverifiers are follow-ups.stellar-accountssupport.Delegated→AddressWithDelegatesrewrite,Externalshape). A live-networkdo_check_authacceptance test is a follow-up.Verification
cargo check -p soroban-cli --all-targetsclean under-D warnings;cargo test -p soroban-cli --lib signer::green (25 tests, incl. 3 new).Note
Filed on the fork so we can cut an RC (
cargo binstall-able) and consume it before/independently of upstreaming.🤖 Generated with Claude Code