Contract 886bcd312f3cf973a8a037479ebf9febe57139e63f109947acce20bd835f3a76

โ† Back to Index ๐Ÿ“ฅ Download WASM

Meta

cliver 26.0.0#60f7458e7ecffddf2f2d91dc6d0d2db4fab03ecc
rssdk_spec_shaking 2
rssdkver 26.1.0#175aa41306f383057a8cdfc84b68d931664fc34e
rsver 1.94.1

Instances

  • CAAIQLZPQRJEOSNHEJ5LUINDNSFX3JGSNIMGEELTW52IQX2BDV5OXSC3
  • CB27DZEFUL4RUZGEXB3SBMVN4W5LXYSGDAPI6VSIS5OT7UC2XDULRYLZ
  • CDTE6DBMGFPTLBLPS7O32FMZGEKUM6KAW7GPACSI6RPY73DDFJIBVL7W

Interface

The Pausable trait signature returns (), not Result, so a caller/owner mismatch can only be signaled by aborting โ€” done via panic_with_error! with this contract's own typed error (matching every other failure path here) rather than a bare string panic!, which surfaced as an untyped trap a client couldn't match against the way it can Error(Contract, #8011).

fn pause(env: soroban_sdk::Env, caller: soroban_sdk::Address)

Emergency freeze. One-way by design: there is no unfreeze() entrypoint. Normal operation is restored only via apply_recovery, so a compromised owner cannot both freeze the account and later unfreeze it unilaterally without going through guardian quorum.

fn freeze(env: soroban_sdk::Env) -> Result<(), SmartAccountTreasuryError>

Returns true if the contract is paused, and false otherwise.

Arguments

  • e - Access to Soroban environment.
fn paused(env: soroban_sdk::Env) -> bool
fn status(env: soroban_sdk::Env) -> Result
fn unpause(env: soroban_sdk::Env, caller: soroban_sdk::Address)

Returns Some(Address) if ownership is set, or None if ownership has been renounced.

Arguments

  • e - Access to the Soroban environment.
fn get_owner(env: soroban_sdk::Env) -> Option

Adds a new policy to an existing context rule, installs it, and returns the assigned policy ID. The policy's install method will be called during this operation.

Arguments

  • e - Access to the Soroban environment.
  • context_rule_id - The ID of the context rule to modify.
  • policy - The address of the policy contract to add.
  • install_param - The installation parameter for the policy.

Errors

  • [SmartAccountError::ContextRuleNotFound] - When no context rule exists with the given ID.
  • [SmartAccountError::DuplicatePolicy] - When the policy already exists in the rule.
  • [SmartAccountError::TooManyPolicies] - When adding would exceed MAX_POLICIES (5).

Events

  • topics - ["policy_added", context_rule_id: u32]
  • data - [policy_id: u32]

Notes

Defaults to requiring authorization from the smart account itself (e.current_contract_address().require_auth()) and then delegating to [storage::add_policy].

fn add_policy(
    env: soroban_sdk::Env,
    context_rule_id: u32,
    policy: soroban_sdk::Address,
    install_param: soroban_sdk::Val,
) -> u32

Adds a new signer to an existing context rule, returning the assigned signer ID.

Arguments

  • e - Access to the Soroban environment.
  • context_rule_id - The ID of the context rule to modify.
  • signer - The signer to add to the context rule.

Errors

  • [SmartAccountError::ContextRuleNotFound] - When no context rule exists with the given ID.
  • [SmartAccountError::DuplicateSigner] - When the signer already exists in the rule.
  • [SmartAccountError::TooManySigners] - When adding would exceed MAX_SIGNERS (15).

Events

  • topics - ["signer_added", context_rule_id: u32]
  • data - [signer_id: u32]

Notes

Defaults to requiring authorization from the smart account itself (e.current_contract_address().require_auth()) and then delegating to [storage::add_signer].

fn add_signer(env: soroban_sdk::Env, context_rule_id: u32, signer: Signer) -> u32

Bootstraps the treasury: sets the owner, seeds a Default context rule with the founding signers/policies (e.g. wallet signers plus a weighted_threshold policy, or passkey External signers), pins the subordinate module addresses, and bootstraps intent_registry in the same call (see below).

This calls the OZ storage primitives directly (ownable::set_owner, add_context_rule) instead of going through their Ownable/ SmartAccount trait entrypoints, both of which require an already-authorized owner/signer to exist โ€” impossible to satisfy before the very first signer is registered.

intent_registry must be a freshly deployed, not-yet-initialized IntentRegistry instance. This function initializes it directly, passing env.current_contract_address() (this treasury) as its admin โ€” which is the whole reason it needs to happen here rather than as a separate call from outside. IntentRegistry::initialize requires admin.require_auth(); with admin set to this treasury's own address and th

fn initialize(
    env: soroban_sdk::Env,
    owner: soroban_sdk::Address,
    initial_signers: soroban_sdk::Vec,
    initial_policies: soroban_sdk::Map,
    config: InitConfig,
) -> Result<(), SmartAccountTreasuryError>
fn __check_auth(
    env: soroban_sdk::Env,
    signature_payload: soroban_sdk::BytesN<32>,
    signatures: AuthPayload,
    auth_contexts: soroban_sdk::Vec,
) -> Result<(), OzSmartAccountError>
fn contract_name(env: soroban_sdk::Env) -> soroban_sdk::Symbol

Retrieves the global registry ID for a policy.

Arguments

  • e - Access to the Soroban environment.
  • policy - The policy address to look up.

Errors

  • [SmartAccountError::PolicyNotFound] - When the policy is not registered in the global registry.
fn get_policy_id(env: soroban_sdk::Env, policy: soroban_sdk::Address) -> u32

Retrieves the global registry ID for a signer.

Arguments

  • e - Access to the Soroban environment.
  • signer - The signer to look up.

Errors

  • [SmartAccountError::SignerNotFound] - When the signer is not registered in the global registry.
fn get_signer_id(env: soroban_sdk::Env, signer: Signer) -> u32
fn is_nonce_used(env: soroban_sdk::Env, nonce: u64) -> bool

Removes a policy from an existing context rule and uninstalls it. The policy's uninstall method will be called during this operation. Removing the last policy is allowed only if the rule has at least one signer.

Arguments

  • e - Access to the Soroban environment.
  • context_rule_id - The ID of the context rule to modify.
  • policy_id - The ID of the policy to remove from the context rule.

Errors

  • [SmartAccountError::ContextRuleNotFound] - When no context rule exists with the given ID.
  • [SmartAccountError::PolicyNotFound] - When the policy doesn't exist in the rule.

Events

  • topics - ["policy_removed", context_rule_id: u32]
  • data - [policy_id: u32]

Notes

Defaults to requiring authorization from the smart account itself (e.current_contract_address().require_auth()) and then delegating to [storage::remove_policy].

fn remove_policy(env: soroban_sdk::Env, context_rule_id: u32, policy_id: u32)

Removes a signer from an existing context rule. Removing the last signer is allowed only if the rule has at least one policy.

Arguments

  • e - Access to the Soroban environment.
  • context_rule_id - The ID of the context rule to modify.
  • signer_id - The ID of the signer to remove from the context rule.

Errors

  • [SmartAccountError::ContextRuleNotFound] - When no context rule exists with the given ID.
  • [SmartAccountError::SignerNotFound] - When the signer doesn't exist in the rule.

Events

  • topics - ["signer_removed", context_rule_id: u32]
  • data - [signer_id: u32]

Notes

Defaults to requiring authorization from the smart account itself (e.current_contract_address().require_auth()) and then delegating to [storage::remove_signer].

fn remove_signer(env: soroban_sdk::Env, context_rule_id: u32, signer_id: u32)

Pulls a finalized recovery outcome from the configured recovery_manager and applies it: force-overwrites ownership, replaces the entire signer/context-rule topology with the guardian-approved replacement, syncs the guardian-freeze epoch, and lifts the freeze. Never pushed by recovery_manager โ€” this contract decides, on its own terms, whether to consume a finalized request, exactly once (AppliedRecovery replay guard). Permissionless: anyone may call this once recovery_manager reports the request finalized, mirroring finalize_recovery's own permissionless design.

Security review finding, two parts. First: this used to overwrite only Ownable's owner. Spend authority โ€” execute_transfer_payment and everything else gated by e.current_contract_address() .require_auth() โ€” is the OZ context-rule/signer registry, a completely separate system owner never gates (see docs/GOVERNANCE_MULTISIG_DESIGN.md ยง9). A compromised payment signer โ€” the more likely real-world compromise, not the own

fn apply_recovery(
    env: soroban_sdk::Env,
    request_id: soroban_sdk::BytesN<32>,
) -> Result

Accepts a pending ownership transfer.

Arguments

  • e - Access to the Soroban environment.

Errors

  • [crate::role_transfer::RoleTransferError::NoPendingTransfer] - If there is no pending transfer to accept.

Events

  • topics - ["ownership_transfer_completed"]
  • data - [new_owner: Address]
fn accept_ownership(env: soroban_sdk::Env)

Creates a new context rule with the specified configuration, returning the newly created ContextRule with a unique ID assigned. Installs all specified policies during creation.

Arguments

  • e - Access to the Soroban environment.
  • context_type - The type of context this rule applies to.
  • name - Human-readable name for the context rule.
  • valid_until - Optional expiration ledger sequence.
  • signers - List of signers authorized by this rule.
  • policies - Map of policy addresses to their installation parameters.

Errors

  • [SmartAccountError::NoSignersAndPolicies] - When both signers and policies are empty.
  • [SmartAccountError::TooManySigners] - When signers exceed MAX_SIGNERS (15).
  • [SmartAccountError::TooManyPolicies] - When policies exceed MAX_POLICIES (5).
  • [SmartAccountError::DuplicateSigner] - When the same signer appears multiple times.
  • [SmartAccountError::PastValidUntil] - When valid_until is in the past.
  • [SmartAccountError::MathOverflow] - When the context rule, si
fn add_context_rule(
    env: soroban_sdk::Env,
    context_type: ContextRuleType,
    name: soroban_sdk::String,
    valid_until: Option,
    signers: soroban_sdk::Vec,
    policies: soroban_sdk::Map,
) -> ContextRule

Retrieves a context rule by its unique ID, returning the ContextRule containing all metadata, signers, and policies.

Arguments

  • e - Access to the Soroban environment.
  • context_rule_id - The unique identifier of the context rule to retrieve.

Errors

  • [SmartAccountError::ContextRuleNotFound] - When no context rule exists with the given ID.
fn get_context_rule(env: soroban_sdk::Env, context_rule_id: u32) -> ContextRule

Renounces ownership of the contract.

Permanently removes the owner, disabling all functions gated by #[only_owner].

Arguments

  • e - Access to the Soroban environment.

Errors

  • [OwnableError::TransferInProgress] - If there is a pending ownership transfer.
  • [OwnableError::OwnerNotSet] - If the owner is not set.

Notes

  • Authorization for the current owner is required.
fn renounce_ownership(env: soroban_sdk::Env)

Initiates a 2-step ownership transfer to a new address.

Requires authorization from the current owner. The new owner must later call accept_ownership() to complete the transfer.

Arguments

  • e - Access to the Soroban environment.
  • new_owner - The proposed new owner.
  • live_until_ledger - Ledger number until which the new owner can accept. A value of 0 cancels any pending transfer.

Errors

  • [OwnableError::OwnerNotSet] - If the owner is not set.
  • [crate::role_transfer::RoleTransferError::NoPendingTransfer] - If trying to cancel a transfer that doesn't exist.
  • [crate::role_transfer::RoleTransferError::InvalidLiveUntilLedger] - If the specified ledger is in the past.
  • [crate::role_transfer::RoleTransferError::InvalidPendingAccount] - If the specified pending account is not the same as the provided new address.

Notes

  • Authorization for the current owner is required.
fn transfer_ownership(
    env: soroban_sdk::Env,
    new_owner: soroban_sdk::Address,
    live_until_ledger: u32,
)

Permissionless TTL maintenance for this contract's instance storage (which PendingAdapter lives in, among other instance-scoped config) โ€” extending TTL creates no authority, matching recovery_manager::extend_ttl's reasoning. Independent security review finding: propose_adapter_change only bumps the instance TTL once, at proposal time; a proposal left unapplied long enough after its delay elapses, on an otherwise fully dormant treasury, could archive before apply_adapter_change is ever called. This lets anyone keep it alive without needing the owner to act.

fn extend_instance_ttl(env: soroban_sdk::Env)

Removes a context rule and cleans up all associated data. This function uninstalls all policies associated with the rule and removes all stored data including signers, policies, and metadata.

Arguments

  • e - Access to the Soroban environment.
  • context_rule_id - The ID of the context rule to remove.

Errors

  • [SmartAccountError::ContextRuleNotFound] - When no context rule exists with the given ID.

Events

  • topics - ["context_rule_removed", context_rule_id: u32]
  • data - []

Notes

Defaults to requiring authorization from the smart account itself (e.current_contract_address().require_auth()) and then delegating to [storage::remove_context_rule].

fn remove_context_rule(env: soroban_sdk::Env, context_rule_id: u32)

Applies a pending adapter change once its delay has elapsed. Permissionless, like apply_recovery/apply_guardian_freeze: the authorization decision (the owner proposing this specific change) already happened at propose_adapter_change time, so when an already-authorized change actually lands needs no further gate to be safe โ€” it only needs the delay to have passed.

fn apply_adapter_change(
    env: soroban_sdk::Env,
    operation: soroban_sdk::Symbol,
) -> Result<(), SmartAccountTreasuryError>

Real gap this session found: docs/TECHNICAL_ARCHITECTURE.md ยง12.7 documents the canonical recovery workflow as starting with "Guardian ... triggers freeze," but freeze() above is owner-gated only, with no guardian-facing path at all โ€” if the owner is the one compromised, guardians had no way to stop ongoing spend authority while a full recovery (quorum + timelock) is still pending. This pulls a guardian-raised freeze flag from recovery_manager the same way apply_recovery pulls a finalized request: permissionlessly, on this contract's own terms, with recovery_manager carrying no knowledge of or dependency on this treasury.

Security review finding, twice over. First revision: this called recovery_manager's read-only guardian_freeze_requested, a flag set once and never cleared โ€” since this entrypoint is deliberately permissionless (matching apply_recovery's "pull an already-authorized fact" pattern), anyone could re-freeze the treasury at any future point, even long after a full recovery ha

fn apply_guardian_freeze(
    env: soroban_sdk::Env,
) -> Result<(), SmartAccountTreasuryError>

Cancels a pending adapter change before it takes effect. Owner-gated, mirroring recovery_manager::cancel_recovery's pattern of letting the legitimate owner walk back an erroneous or no-longer-wanted proposal before its delay elapses.

fn cancel_adapter_change(
    env: soroban_sdk::Env,
    operation: soroban_sdk::Symbol,
) -> Result<(), SmartAccountTreasuryError>

Executes a one-to-many SAC split from the treasury. Each recipient is independently policy-checked (own recipient-allowlist entry, own per-recipient amount cap) before the adapter call, so a split cannot be used to move funds to a disallowed recipient by hiding it inside an aggregate-approved total.

fn execute_split_payment(
    env: soroban_sdk::Env,
    asset: soroban_sdk::Address,
    recipients: soroban_sdk::Vec,
    amounts: soroban_sdk::Vec,
    nonce: u64,
    expected_policy_version: u32,
) -> Result<(), SmartAccountTreasuryError>

Proposes rewiring which adapter contract handles a given operation. Owner-gated. Unlike policy_engine / intent_registry / recovery_manager (pinned at initialize, see Architecture Decision in docs/TECHNICAL_ARCHITECTURE.md ยง6.9), adapters are mutable post-init because the doc explicitly scopes set_adapter_config as an owner-configurable entrypoint.

This does not take effect immediately โ€” see apply_adapter_change. An independent security review (docs/SMART_CONTRACT_AUDIT_REPORT.md finding 3) noted that immediate, undelayed reconfiguration of execution routing is a meaningful risk for a treasury contract specifically (it decides where funds go, unlike e.g. freeze, which only stops movement and is deliberately kept immediate). Overwrites any existing pending proposal for the same operation โ€” the most recent proposal wins, matching how re-proposing normally works elsewhere in this workspace (e.g. policy_engine::set_asset_rule).

Explicitly extends the instance TTL on write. Review findi

fn propose_adapter_change(
    env: soroban_sdk::Env,
    operation: soroban_sdk::Symbol,
    adapter: soroban_sdk::Address,
) -> Result<(), SmartAccountTreasuryError>

Retrieves the number of all context rules, including expired rules. Defaults to 0.

Arguments

  • e - Access to the Soroban environment.
fn get_context_rules_count(env: soroban_sdk::Env) -> u32

Cancels a previously created scheduled payment. Requires this contract's own authorization, same as creation.

This closes a real gap: intent_registry::cancel_intent exists and is ensure_admin-gated, but intent_registry's configured admin is this contract's own address โ€” before this entrypoint existed, there was no way for anyone, including the treasury's own signers, to ever call it, since only smart_account itself can satisfy that admin check via a nested sub-invocation.

fn cancel_scheduled_payment(
    env: soroban_sdk::Env,
    intent_id: soroban_sdk::BytesN<32>,
) -> Result<(), SmartAccountTreasuryError>

Creates a scheduled/recurring payment intent. Requires this contract's own authorization (same signer/context-rule/policy model as interactive payments), then delegates canonical lifecycle and replay state to intent_registry โ€” see Architecture Decision 2. intent_registry must have been configured with this contract's own address as its admin, so this nested call succeeds via sub-invocation tree membership rather than a second signature.

The caller-supplied policy_version and adapter on intent are both ignored and overwritten: policy_version with policy_engine's current version, and adapter with the transfer_adapter currently configured (via propose_adapter_change/apply_adapter_change). Both must reflect what was actually in effect when the signer approved this schedule, not a value the caller chose โ€” otherwise a later adapter reconfiguration could silently redirect an already-approved scheduled payment through a different adapter at execution time (see execute_scheduled_payment fo

fn create_scheduled_payment(
    env: soroban_sdk::Env,
    intent: ScheduledIntentArgs,
) -> Result<(), SmartAccountTreasuryError>

Executes a single-recipient SAC payment. Requires this contract's own authorization (delegated entirely to the OZ context-rule/signer/ policy model via __check_auth), then treasury policy, then a narrowly preauthorized adapter call.

fn execute_transfer_payment(
    env: soroban_sdk::Env,
    asset: soroban_sdk::Address,
    destination: soroban_sdk::Address,
    amount: i128,
    nonce: u64,
    expected_policy_version: u32,
) -> Result<(), SmartAccountTreasuryError>

Updates the name of an existing context rule, returning the updated ContextRule with the new name.

Arguments

  • e - Access to the Soroban environment.
  • context_rule_id - The ID of the context rule to update.
  • name - The new human-readable name for the context rule.

Errors

  • [SmartAccountError::ContextRuleNotFound] - When no context rule exists with the given ID.

Events

  • topics - ["context_rule_meta_updated", context_rule_id: u32]
  • data - [name: String, context_type: ContextRuleType, valid_until: Option<u32>]

Notes

Defaults to requiring authorization from the smart account itself (e.current_contract_address().require_auth()) and then delegating to [storage::update_context_rule_name].

fn update_context_rule_name(
    env: soroban_sdk::Env,
    context_rule_id: u32,
    name: soroban_sdk::String,
) -> ContextRule

Executes an already-approved scheduled payment. Permissionless by design (matches recovery_manager::finalize_recovery): the signer approval already happened at create_scheduled_payment time. Safety here comes entirely from intent_registry's own execution-window, cancellation, and cumulative-usage checks โ€” not from caller identity โ€” except that intent_registry.mark_child_executed itself still requires its configured Executor address to authorize the call, so an operational relayer key (not a treasury signer) gates when within the valid window execution happens.

Deliberately takes only intent_id and child_sequence from the caller. An earlier revision also accepted asset, destination, amount, and expected_policy_version as caller-supplied parameters and used them directly for the policy check and adapter dispatch โ€” since this entrypoint has no require_auth() gate of its own (by design, see above) and the relayer role is explicitly untrusted (docs/TECHNICAL_ARCHITECTURE.md ยง13.

fn execute_scheduled_payment(
    env: soroban_sdk::Env,
    intent_id: soroban_sdk::BytesN<32>,
    child_sequence: u32,
) -> Result<(), SmartAccountTreasuryError>

Updates the expiration time of an existing context rule, returning the updated ContextRule with the new expiration time.

Arguments

  • e - Access to the Soroban environment.
  • context_rule_id - The ID of the context rule to update.
  • valid_until - New optional expiration ledger sequence. Use None for no expiration.

Errors

  • [SmartAccountError::ContextRuleNotFound] - When no context rule exists with the given ID.
  • [SmartAccountError::PastValidUntil] - When valid_until is in the past.

Events

  • topics - ["context_rule_meta_updated", context_rule_id: u32]
  • data - [name: String, context_type: ContextRuleType, valid_until: Option<u32>]

Notes

Defaults to requiring authorization from the smart account itself (e.current_contract_address().require_auth()) and then delegating to [storage::update_context_rule_valid_until].

fn update_context_rule_valid_until(
    env: soroban_sdk::Env,
    context_rule_id: u32,
    valid_until: Option,
) -> ContextRule

Imports

WebAssembly Text (WAT) โ–ถ