Skip to content

03 — Agent authority & wallet modes

This is the heart of OAW. Everything else (venue binding, funding, execution) hangs off one question: what is the agent's spending authority, and who can create it?

1. AgentAuthority — the one parent object

There is exactly one active authority per agent, regardless of how many venues it binds or how many chains it touches. Provider- and venue-agnostic shape (reference: EconomyOSAgentProfile; serialized by economyos_agent_wallet_service.serialize_profile):

jsonc
{
  "id": "uuid",
  "user_id": "uuid",
  "agent_id": "string|null",
  "environment": "production|testnet|...",

  "authority_source": "provider_managed | virtuals_linked | external_imported | unknown",
  "wallet_provider":  "privy | virtuals | external | unknown",
  "wallet_address":   "0x… | <solana pubkey> | …",
  "provider_wallet_id": "opaque-id|null",        // e.g. privy_agent_wallet_id
  "virtuals_agent_id":  "id|null",
  "proof_state":      "verified | pending | unverified | not_applicable",
  "signer_status":    "ready | required | pending | unsupported | unknown",
  "supported_chains": ["base","ethereum","arbitrum","polygon","solana", "..."],

  "policy_ref": "uuid|null",                      // caps, autonomy mode, blocks
  "metadata":   { /* non-secret; e.g. pending_virtuals_link flags */ }
}

Hard rules (carried verbatim from the reference implementation):

  • It never holds raw key material. It holds an opaque provider id; the provider adapter resolves signing.
  • wallet_provider="virtuals" is set as active authority only afterproof_state == "verified". A pending Virtuals link is non-authoritative.
  • A ready provider-managed (Mode A) authority is not overwritten by a Virtuals link unless switch_authority=True and ownership proof passes.

2. The provider abstraction

A wallet provider is anything that can: create/load an agent wallet, expose its address + supported chains, sign a transaction/typed-data under policy, and (optionally) expose richer capabilities (caps, agentmail, card, tokenization, ACP jobs). OAW defines a WalletProviderAdapter interface (full signatures in 07); the two production adapters are Privy and Virtuals.

2.1 Privy — the wallet types and how OAW uses each

Privy is not one wallet; it's a family. OAW maps them deliberately:

Privy primitiveWhat it isOAW role
Auth (social/email/OAuth)Login + identityUser auth; ties the human to the agent. Not spending authority.
Embedded walletUser-controlled, user signsGood for user actions; not autonomous (needs the user present each sign).
Server walletBackend-controlled walletBuilding block for app treasury.
Server wallet v2 / agent walletProgrammatic, policy-bounded, backend-controlledThis is Mode A. The autonomous agent's spending authority. Created headlessly by the dev backend.
useDepositAddress (chain-agnostic deposit address)Privy generates a unique deposit address; user sends crypto from any supported source chain; funds auto-bridge/swap to the destination walletA funding-layer primitive (05). The "deposit USDC anywhere → agent wallet" rail.

Key fact that makes the whole design coherent: Virtuals' EconomyOS agent wallets are themselves built on Privy wallet infrastructure. So a Privy agent wallet (Mode A) and a Virtuals agent wallet (Mode B) share the same underlying signer substrate. The signer path is uniform; what differs is who provisioned the wallet and what extra capabilities ride on top (caps/agentmail/card/ACP).

2.2 Virtuals — what the EconomyOS agent wallet adds

A Virtuals agent wallet is a Privy wallet plus an EconomyOS/ACP envelope:

  • Native spending caps & policies (set in the Virtuals portal, or programmatically where exposed).
  • Agentmail — an email identity for the agent.
  • Stripe-powered virtual card — fiat spend rail.
  • ACP identity — the agent's identity in the Agent Commerce Protocol marketplace (jobs: create/fund/complete).
  • Tokenization — an agent token / token launch.
  • DegenClaw / Arena — a Virtuals-routed execution venue (perps/spot via Hyperliquid under the hood).
  • Virtuals compute credits — buy compute right on the agent wallet portal.

OAW's Virtuals adapter wraps the verified ACP sidecar client (virtuals_os_acp_v3_client.py), whose actual surface is:

verify_identity            signer_status            verify_authority_proof
wallet_balance             create_job               create_fund_transfer_job
fund_job                   complete_job             reject_job
send_message               submit_deliverable       drain_events
get_job                    list_resources
dgclaw_join                dgclaw_activate_unified   dgclaw_add_api_wallet
dgclaw_deposit             dgclaw_withdraw           dgclaw_trade
dgclaw_status              dgclaw_receipts

Note what is absent: there is no create_agent / create_agent_wallet. This client operates an existing Virtuals agent. It cannot mint one.

3. The decisive question, fully resolved

Can a third-party developer's backend create a Virtuals/EconomyOS agent wallet headlessly, server-side?

3.1 The evidence

  1. The verified ACP client has no creation method. It has verify/operate/job/ fund/dgclaw methods only (list above). Creation is conspicuously absent.
  2. The documented creation flow is an owner/portal flow:app.virtuals.io/acp/new — a human, in a browser, provisions the agent (which is what sets up the native caps / agentmail / Stripe card / ACP identity / tokenization).
  3. The reference docs explicitly forbid faking it server-side: "do not fabricate server-side identity/signers," "do not shell out to acp-cli in production." The CLI exists but is a dev harness, not a production creation surface.

3.2 The precise conclusion (use this wording)

No production-safe, server-side Virtuals agent-wallet creation surface has been verified in the current inspected SDK / CLI / docs. The verified production-safe path today is owner/user creation or approval through Virtuals/ACP flows, after which a developer backend links and operates that wallet. If Virtuals later exposes a server-safe creation or registration API, OAW adds Mode C / Mode D.

This is deliberately not "Virtuals cannot do programmatic creation." It is "we have not verified a backend-safe creation API, and the available surface points at owner/handoff flows." Keeping that door open is the entire premise of the Virtuals PR (09).

3.3 Why this matters for devs

Because creation is not headless, a dev cannot assume "every user instantly has a Virtuals agent wallet." They must:

  • Mode A: give every user a programmatic provider wallet (Privy agent wallet) instantly — the autonomous default.
  • Mode B: link the user's existing Virtuals wallet when they have one, to inherit the native caps/agentmail/card/ACP.

That is not a compromise — it is the correct production stance until Modes C/D exist.

4. The wallet modes (A–E)

Modeauthority_sourcewallet_providerWho createsCaps/agentmail/card/ACPStatus
Aprovider_managedprivyDev backend, programmaticNo (provider-native policy only)Production
Bvirtuals_linkedvirtualsUser, in Virtuals portalYes, inheritedProduction
Cprovider_managed (→virtuals)virtualsVirtuals server APIYesUnverified — PR target
Dvirtuals_linkedvirtualsDev's Privy wallet registered into VirtualsYesUnverified — PR target
Eexternal_importedexternalUser (own key / capped signer)NoScaffolded

Mode A — provider-managed (Privy) agent wallet [VERIFIED / SHIPPED]

Reference: economyos_agent_wallet_service.ensure_agent_wallet(...).

  • Fully programmatic: creates or loads a Privy agent wallet, stores wallet_provider="privy", provider_wallet_id, wallet_address.
  • The autonomous-from-chat default. Any user gets one instantly on explicit bind/setup intent.
  • Missing Virtuals identity is non-fatal — Mode A is sufficient authority for Polymarket, Hyperliquid, etc.
  • Idempotent; no secrets in responses.

Mode B — linked existing Virtuals/EconomyOS wallet [VERIFIED / SHIPPED]

Reference: economyos_agent_wallet_service.link_existing_virtuals_agent(...).

  • The user already created the agent on Virtuals.io. OAW links it.
  • Verification is not ownership. verify_identity only proves the agent exists. verify_authority_proof proves the user controls it. wallet_provider becomes "virtuals" (active authority) only after proof passes.
  • expected_wallet_address is a safety check, not proof.
  • Missing ownership proof → a pending, non-authoritative link.
  • Mode B never provisions a Privy wallet, and never silently falls back to Mode A.
  • Inherits Virtuals' native caps/agentmail/card/ACP/tokenization.

Mode C — Virtuals programmatic create [UNVERIFIED — do not implement]

A hypothetical Virtuals server API that mints an agent wallet (with its EconomyOS envelope) from a dev backend. Not present in inspected code/docs. If Virtuals ships it, OAW's Virtuals adapter gains a create_agent_wallet() and Mode A/Mode B converge: every user could get a Virtuals agent wallet instantly. This is the primary ask in 09.

Mode D — provider wallet registered into Virtuals [UNVERIFIED — do not implement]

A hypothetical Virtuals API to register/adopt an external (Privy) wallet into EconomyOS, so a Mode-A wallet gains the Virtuals envelope without re-creating it. The cleanest convergence — the dev keeps Privy as the signer substrate and gets the EconomyOS capabilities. Second ask in 09.

Mode E — external imported [SCAFFOLDED]

A user-owned wallet / capped delegated signer (Model 2 trading wallet). Fields may exist; the full flow is out of scope until explicitly scoped. Useful for "non-custodial power user with a hardware key" patterns.

5. The authority resolution algorithm

This is what every venue bind and every funding/execution call runs first (the pre-bind gate, 04, implements it). Pulled from venue_prebind_gate.ensure_prebind_authority:

resolve_authority(user, venue, action, allow_auto_create):
  contract = registry[venue]
  if contract is None: return venue_contract_missing
  if not contract.per_user_authority_required: return authority_ready

  profile = get_profile(user)
  if not wallet_ready(profile):
      if not allow_auto_create: return agent_wallet_required        # reads never create
      ensure_agent_wallet(user)                                     # Mode A auto-create
      profile = reload(); if not wallet_ready: return agent_wallet_required
      created = True

  provider = profile.wallet_provider
  if provider == "virtuals" and profile.proof_state != "verified":
      return authority_reconciliation_required                      # Mode B integrity
  if provider not in {"privy","virtuals"}:
      return unsupported_authority_source

  if venue in ACP_NATIVE_VENUES:                                    # virtuals, degenclaw
      if not virtuals_identity_linked(profile):
          return owner_handoff_required if pending_link else virtuals_identity_required
      if not virtuals_signer_ready(profile):
          return virtuals_signer_required

  return authority_ready (or agent_wallet_created if created)

Key invariants this enforces:

  • Reads never create wallets. Only explicit write intent (allow_auto_create=True) can mint a Mode A wallet.
  • Mode B cannot be auto-created, and an unverified Virtuals profile blocks rather than degrading to Mode A.
  • ACP-native venues (Virtuals, DegenClaw) additionally require linked Virtuals identity + ready signer; non-ACP venues do not.

6. What a dev gets from L1

python
authority = await oaw.authority.ensure(user, allow_auto_create=True)   # Mode A
# or
authority = await oaw.authority.link_virtuals(user, virtuals_agent_id, proof)  # Mode B

authority.wallet_address      # fund here
authority.wallet_provider     # privy | virtuals
authority.supported_chains    # for funding routing
authority.execution_route()   # privy_agent_wallet | virtuals_acp_sidecar | unsupported

L1 gives the agent a wallet. L2 (04) binds that wallet to venues.

Released under the Apache-2.0 License.