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):
{
"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=Trueand 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 primitive | What it is | OAW role |
|---|---|---|
| Auth (social/email/OAuth) | Login + identity | User auth; ties the human to the agent. Not spending authority. |
| Embedded wallet | User-controlled, user signs | Good for user actions; not autonomous (needs the user present each sign). |
| Server wallet | Backend-controlled wallet | Building block for app treasury. |
| Server wallet v2 / agent wallet | Programmatic, policy-bounded, backend-controlled | This 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 wallet | A 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_receiptsNote 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
- The verified ACP client has no creation method. It has verify/operate/job/ fund/dgclaw methods only (list above). Creation is conspicuously absent.
- 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). - The reference docs explicitly forbid faking it server-side: "do not fabricate server-side identity/signers," "do not shell out to
acp-cliin 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)
| Mode | authority_source | wallet_provider | Who creates | Caps/agentmail/card/ACP | Status |
|---|---|---|---|---|---|
| A | provider_managed | privy | Dev backend, programmatic | No (provider-native policy only) | Production |
| B | virtuals_linked | virtuals | User, in Virtuals portal | Yes, inherited | Production |
| C | provider_managed (→virtuals) | virtuals | Virtuals server API | Yes | Unverified — PR target |
| D | virtuals_linked | virtuals | Dev's Privy wallet registered into Virtuals | Yes | Unverified — PR target |
| E | external_imported | external | User (own key / capped signer) | No | Scaffolded |
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_identityonly proves the agent exists.verify_authority_proofproves the user controls it.wallet_providerbecomes"virtuals"(active authority) only after proof passes. expected_wallet_addressis 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
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 | unsupportedL1 gives the agent a wallet. L2 (04) binds that wallet to venues.