SECURITY_AUDIT C2 · guarded no-op kept only to default-deny a stray
init call. Initialization is now atomic via __constructor, so by the
time the contract exists PasskeyPubkey is always present. Any direct
init invocation — e.g. a front-runner attempting the old C2 exploit —
therefore always errors AlreadyInitialized. It can never (re)claim
ownership or reset state.
fn init(
env: soroban_sdk::Env,
passkey_pubkey: soroban_sdk::BytesN<65>,
passkey_cred_id: soroban_sdk::BytesN<32>,
admin: soroban_sdk::Address,
)
Read-only accessor used by the frontend to render the four-guarantee
panel. Returns the policy as stored or panics with PolicyNotFound.
fn get_policy(env: soroban_sdk::Env, merchant: soroban_sdk::Address) -> Policy
Custom account interface (CAP-46-11). Called by the Soroban host on every auth attempt where this contract is the authorizing principal.
Authorization rule (SECURITY_AUDIT A1 — dispatch on credential FIRST, exactly one authorization model per entry):
WalletAuth::Agent: authenticate the ed25519 session key, then
authorize EVERY context against the session's allowlist + windowed
budget. This path NEVER runs the pull-policy loop, so an agent
credential can never mutate policy last_charge_at.WalletAuth::Passkey: first try the pull-policy path — if every
auth_context matches an active on-chain policy (a
token.transfer(this_wallet, merchant, amount) with a non-revoked,
non-expired policy, amount <= max_per_charge, interval elapsed),
authorize without consulting the signature, bumping last_charge_at
per matched policy. Otherwise verify the passkey secp256r1 signature
over signature_payload (panics on failure → host auth rejection).fn __check_auth(
env: soroban_sdk::Env,
signature_payload: soroban_sdk::BytesN<32>,
auth: WalletAuth,
auth_contexts: soroban_sdk::Vec,
) -> Result<(), soroban_sdk::Error>
SECURITY_AUDIT C2 · atomic deploy+init constructor. Soroban runs
__constructor exactly once, in the SAME transaction as the deploy that
creates the contract. There is therefore NO un-inited window between
deploy and init for an observer to front-run with their own passkey +
admin (the C2 finding). The wallet is fully owned by the deployer-chosen
principals the instant it exists on-chain.
Args:
passkey_pubkey: the passkey's secp256r1 public key (65-byte
uncompressed X9.62 = 0x04 || X || Y).passkey_cred_id: the WebAuthn credential id.admin: gates install_policy / revoke_policy / agent-session
mutations in v0.1. For the spike, callers pass the deployer's classic
G-account so the trusted-setup server can sign these. v0.2 migrates the
admin to the wallet's own contract address so install/revoke flow back
through __check_auth and are gated by the user's passkey.max_absolute_per_charge: SECURITY_AUDIT C3 · the IMMUTABLE absolute
ceiling on any single per-charge amount/cap. Set once herefn __constructor(
env: soroban_sdk::Env,
passkey_pubkey: soroban_sdk::BytesN<65>,
passkey_cred_id: soroban_sdk::BytesN<32>,
admin: soroban_sdk::Address,
max_absolute_per_charge: i128,
)
User-controlled kill switch. After this call, all further merchant
pulls fail authorization until install_policy is called again with
a fresh passkey signature.
fn revoke_policy(env: soroban_sdk::Env, merchant: soroban_sdk::Address)
Install (or replace) a spending policy for a specific merchant. Requires
the wallet's own auth — i.e., a passkey signature validated by
__check_auth. This is the only path to grant a merchant the right
to pull funds.
fn install_policy(
env: soroban_sdk::Env,
merchant: soroban_sdk::Address,
token: soroban_sdk::Address,
amount_per_charge: i128,
max_per_charge: i128,
interval_seconds: u64,
expires_at: u64,
)
Read-only accessor for a delegated agent session.
fn get_agent_session(
env: soroban_sdk::Env,
session_pubkey: soroban_sdk::BytesN<32>,
) -> AgentSession
User-controlled kill switch for a delegated agent session. After this call, all agent transfers under this key fail until the session is re-installed.
fn revoke_agent_session(env: soroban_sdk::Env, session_pubkey: soroban_sdk::BytesN<32>)
Install (or replace) a delegated agent spending session. Gated by the
same admin as install_policy in v0.1.
fn install_agent_session(
env: soroban_sdk::Env,
session_pubkey: soroban_sdk::BytesN<32>,
token: soroban_sdk::Address,
per_tx_cap: i128,
window_seconds: u64,
window_cap: i128,
expires_at: u64,
allow_recipients: soroban_sdk::Vec,
ssl_hash: soroban_sdk::BytesN<32>,
)