fn get(env: soroban_sdk::Env, id: soroban_sdk::BytesN<32>) -> Subscription
fn pause(env: soroban_sdk::Env, id: soroban_sdk::BytesN<32>)
fn cancel(env: soroban_sdk::Env, id: soroban_sdk::BytesN<32>)
Trigger the next charge.
v0.1 auth model (audit-002 F4): the buyer must sign every charge.
buyer.require_auth() is called below. An off-chain scheduler can
submit the transaction, but the buyer must produce a fresh signature
each time — via smart-wallet session, WalletConnect, or equivalent.
v0.2 will replace this with a pre-auth allowance primitive.
fn charge(env: soroban_sdk::Env, id: soroban_sdk::BytesN<32>) -> u64
Buyer authorizes a new subscription. Returns a deterministic 32-byte id. Both buyer and contract authentication are required at the host level (require_auth invocations).
fn create(
env: soroban_sdk::Env,
buyer: soroban_sdk::Address,
merchant: soroban_sdk::Address,
token: soroban_sdk::Address,
amount: i128,
period_seconds: u64,
max_periods: u32,
expires_at: u64,
nonce: soroban_sdk::BytesN<32>,
) -> soroban_sdk::BytesN<32>
fn resume(env: soroban_sdk::Env, id: soroban_sdk::BytesN<32>)
v0.2 autonomous charge — NO buyer signature at charge time.
Closes the audit-002 F4 pre-auth gap. Instead of buyer.require_auth
transfer (which forces a fresh buyer signature every period), this
pulls amount from the buyer's standing SEP-41 allowance via
transfer_from, with this contract as the spender. The buyer signs
ONCE, off-band: token.approve(buyer, <this contract>, cap, expiry).
Thereafter any party (an off-chain scheduler/relayer that pays the tx
fee, never custodies funds) can submit autocharge(id) each period.Bounds are enforced on two independent layers:
transfer_from fails and the
buyer must re-approve — a hard, on-chain spending ceiling.Non-custodial: funds move buyer -> merchant directly; the contract only holds the spender role, never the balance.
fn autocharge(env: soroban_sdk::Env, id: soroban_sdk::BytesN<32>) -> u64
Mark a subscription as Expired if its terminal conditions hold (expires_at passed OR max_periods reached). Anyone can call; idempotent. Returns true if state was changed, false otherwise. This exists because charge() cannot persist a status change while also panicking — Soroban panics revert state.
fn mark_expired(env: soroban_sdk::Env, id: soroban_sdk::BytesN<32>) -> bool
Bind an integrity attester (ed25519 public key) to a subscription. The
merchant sets it; thereafter autocharge_attested is the only autonomous
path that settles, and it requires a fresh signature from this key.
fn set_attester(
env: soroban_sdk::Env,
id: soroban_sdk::BytesN<32>,
attester: soroban_sdk::BytesN<32>,
)
Deploy-time, immutable platform fee config. fee_bps (basis points,
297 = 2.97%, max 1000 = 10%) is taken out of every autonomous charge
(autocharge / autocharge_attested) and routed to platform; the merchant
receives amount - fee. fee_bps = 0 means no fee leg (the rail runs free).
Set atomically at deploy so there is no front-running window on a public
network — the fee recipient and rate are bound to the contract instance.
fn __constructor(env: soroban_sdk::Env, platform: soroban_sdk::Address, fee_bps: u32)
v0.3 attested autonomous charge — the integrity gate, on-chain.
Autonomous debit that REFUSES to settle without a fresh, valid integrity
attestation. The off-chain attester (which actually determines whether the
requesting agent is compromised) signs id || not_after with the ed25519
key bound via set_attester. This contract verifies that signature on the
host, checks freshness against ledger time, and only then pulls the charge.
What this guarantees on-chain: no settlement without a fresh signed attestation bound to THIS subscription (no cross-sub replay; expiry enforced). What it does NOT do: detect compromise itself — that is the attester's job. The contract makes the attestation inescapable, not optional. x402/AP2 settle on authorization alone; this refuses.
Fail-closed: no attester set → AttesterNotSet; expired → AttestationExpired; bad signature → ed25519_verify traps (reverts).
fn autocharge_attested(
env: soroban_sdk::Env,
id: soroban_sdk::BytesN<32>,
not_after: u64,
signature: soroban_sdk::BytesN<64>,
) -> u64