Skip to content
forage.
← All resources

Under the surface

Accounts & mirrors

Individual ownership, isolated account instances and bounded cross-chain control.

September 5, 202617 min read
Product direction. Not a live offering.

Financial capabilities, provider access and coverage require separate approvals. Examples are illustrative, not promises of returns.

Status: canonical product model. This specification supersedes earlier YET/Wry account diagrams and every ERC-6551 or pooled portfolio proposal.

The accepted Clubs and Harvest extension reuses this hierarchy for managed strategies. A Club is a community, not an owner or pool. Operator collateral and coverage reservations are separate from customer assets and mirror budgets; the extension does not enable new value-moving code.

Customer-facing presentation follows the end-user experience specification. Canonical Account, Account Instance, and Account Mirror are implementation and advanced-audit concepts; ordinary tasks must not require customers to understand them.

1. Product decision

The central customer promise is earning money and creating, holding and trading personal asset Bundles within their own subaccounts. Bundle is the customer name for the existing Personal Basket, with explicit buy/add/sell/rebalance workflows. See the earning and asset-bundle specification. The account architecture implements that product rather than becoming the main customer task.

Forage uses the useful product shape of StonkBrokers—visible, addressable containers with isolated assets and activity—but replaces NFT ownership and token-bound accounts with verified-customer ownership, AGW recovery, programmable smart accounts, bounded delegation, and chain-agnostic account instances.

The system is built around four separations:

  1. A Portfolio is a logical customer workspace and aggregation view.
  2. A Canonical Account is a chain-agnostic lane with one beneficial owner and one purpose.
  3. An Account Instance is the real smart account, provider wallet, or provider book entry on one chain/provider rail.
  4. An Account Mirror synchronizes bounded authority and budget to a remote instance; it never represents assets.

The customer experience is “one portfolio made of inspectable accounts.” The implementation is never “one wallet pretending every provider and chain has the same custody, rights, or authority.”

2. What we borrow from StonkBrokers

StonkBrokers gives each NFT a distinct ERC-6551 account whose contents travel with NFT ownership. Forage borrows:

  • one recognizable container per account lane;
  • an addressable account with its own assets and history;
  • strong isolation between sibling containers;
  • visible composition and activity;
  • deterministic discovery of the account associated with a container.

Forage does not borrow:

  • an ERC-721 as the ownership root;
  • bearer transfer of the container and all contained assets;
  • arbitrary token-bound execution;
  • NFT marketplace or AMM transfer semantics;
  • one EVM-only identity scheme presented as chain agnostic;
  • rewards, distributions, or permissions inherited from owning a collectible.

This is a deliberate product distinction. The official StonkBrokers documentation describes its broker NFT and token-bound wallet as traveling together. That behavior is useful for a collectible; it is unsafe as the default ownership model for regulated customer accounts.

3. Canonical hierarchy

Verified Customer or Organization
  |
  +-- Root Authority
  |     Abstract Global Wallet (AGW)
  |     passkey ownership + recovery + root session creation
  |
  +-- Portfolio: Long-term
  |     |
  |     +-- Canonical Account: Dinari U.S. provider lane
  |     |     +-- Instance: provider-approved wallet on chain A
  |     |     +-- Provider Account: authoritative brokerage record
  |     |
  |     +-- Canonical Account: Personal Robinhood basket
  |     |     +-- Mirror: bounded control state for chain 4663
  |     |     +-- Instance: customer-owned Robinhood Chain smart account
  |     |
  |     +-- Canonical Account: recurring strategy
  |     |     +-- Instance: Abstract strategy account
  |     |     +-- Mandate: selected asset, cadence, caps, expiry
  |     |
  |     +-- Canonical Account: AI sandbox
  |           +-- Instance: isolated agent account
  |           +-- Delegate: one mandate, one budget, short expiry
  |
  +-- Portfolio: Income
        +-- its own accounts, instances, mirrors, and mandates

The Portfolio node does not own or pool funds. It groups accounts for display, policy, reporting, and customer intent. Assets exist only in an Account Instance or an authoritative provider account.

4. Core object model

ObjectIdentityAuthorityMay hold assets?Notes
Legal AccountlegalAccountIdVerified individual or organizationNoKYC/KYB, jurisdiction, beneficial owner, authorized actors
Root AuthorityrootAuthorityIdAGW owner/recovery policyNo by product designRoot authorization and recovery surface
PortfolioportfolioIdLegal AccountNoLogical aggregation and intent boundary
Canonical AccountaccountIdLegal Account through AGWNoChain-agnostic purpose and policy identity
Account InstanceinstanceIdChain/provider-specific owner modelYes, or references provider assetsOne actual settlement/custody location
Provider AccountproviderAccountIdProvider and verified customerProvider-authoritativeOrders, statements, corporate actions, eligibility
Wallet BindingwalletBindingIdProvider-approved signed bindingNoConnects provider account to one approved Account Instance
Account MirrormirrorIdRoot Router + local Mirror ControllerNoControl state, allocation, expiry, nonces, pause
MandatemandateIdCustomer signatureNoBounded permission targeting one or more explicit instances
DelegatedelegateIdMandate + local account policyNoShort-lived execution signer; never the owner
Positioncomposite provider/chain keyProvider or chain stateN/ANever inferred from mirror state
Reconciliation RecordreconciliationIdSystem/audit processNoCompares provider, chain, and Forage state

Cardinality:

LegalAccount 1 ---- N RootAuthorities      normally one active AGW, historical rotations retained
LegalAccount 1 ---- N Portfolios
Portfolio    1 ---- N CanonicalAccounts
CanonicalAccount 1 ---- N AccountInstances
AccountInstance 1 ---- 0..1 active Mirror     only when remote delegated control is enabled
AccountInstance 1 ---- N Mandates
Mandate 1 ---- N DelegateGenerations      at most one active delegate per chain unless explicitly approved
ProviderAccount 1 ---- 1 active WalletBinding where the provider requires uniqueness

Separate legal entities controlled by the same person receive separate Legal Accounts and account namespaces. They are not sibling portfolios.

5. Account classes

A funded Bundle binds to the customer's actual BASKET instance where the provider permits independent smart-account custody, or to an approved dedicated provider account. Any same-customer accounting partition requires exclusive unit/lot allocation and explicit disclosure that it lacks independent onchain custody. A provider that cannot support the requested isolation cannot be made to support it through a visual label.

The rewards and income architecture adds merchant entitlements outside the customer investment hierarchy. A rewards ledger is not an AGW wallet or brokerage account. A group trail shares progress without shared custody. Financial accounts are provisioned when needed, not as a prerequisite for browsing offers.

ClassCustomer or protocol namespacePurposePrincipal holder
PROVIDERCustomerOne regulated provider/account laneProvider-recognized customer wallet or provider book
DIRECTCustomerGeneral direct onchain holdingsCustomer Account Instance
BASKETCustomerChain-local constituents governed by a basket policyCustomer Account Instance
STRATEGYCustomerCapital and instruments for one bounded strategyCustomer Account Instance or provider account
AGENTCustomerSmall, isolated budget for one AI/automation purposeCustomer Account Instance
CASHCustomerFunding and settlement assets where permittedCustomer or provider account
PROMOTIONProtocol onlyPre-funded promotional liabilitiesProtocol promotion entity/account
REVENUEProtocol onlyEarned protocol revenueProtocol treasury/accounting entity

PROMOTION and REVENUE cannot be created under a customer namespace. Customer accounts cannot be treated as protocol treasury accounts. Transfers between namespaces require a normal, explicit, auditable transaction; hierarchy never grants sibling access.

An income investment uses the provider/instrument class that reflects its actual legal form. CASH does not imply that a money-market fund, tokenized Treasury or stablecoin is an insured deposit. Customer interest/dividends are attributed to the exact earning position and never appear in protocol revenue. A reward-funded contribution needs payout and provider funding evidence; a mirror cannot manufacture buying power.

6. Chain-agnostic identity

Canonical account ID

The canonical ID intentionally excludes chainId and wallet address:

accountId = keccak256(
  abi.encode(
    "FORAGE_ACCOUNT_V2",
    legalAccountIdHash,
    portfolioId,
    accountClass,
    providerNamespaceHash,   // zero for provider-neutral lanes
    purposeHash,
    accountNonce
  )
)

Properties:

  • stable across chain deployments and provider-approved wallet replacements;
  • unique within one Legal Account namespace;
  • contains no PII;
  • cannot be sold or reassigned to a different beneficial owner;
  • remains stable while Account Instances are added, rotated, frozen, or retired.

Because portfolioId is part of the namespace, a Canonical Account is not dragged between Portfolios. Reorganization creates a new account under the destination Portfolio and uses an explicit, eligible asset/provider transfer; the former account remains in history.

Chain reference

Use a CAIP-2-style namespace rather than assuming every network has an EIP-155 integer:

chainRef = "eip155:4663"          // Robinhood Chain
chainRef = "eip155:2741"          // Abstract
chainRef = "solana:<genesisHash>" // Solana cluster
chainRef = "provider:dinari-us"   // provider book or managed wallet lane

The production registry stores a normalized chainRefHash plus the human-readable, versioned network record.

Account instance ID

instanceId = keccak256(
  abi.encode(
    "FORAGE_ACCOUNT_INSTANCE_V1",
    accountId,
    chainRefHash,
    providerIdHash,
    custodyMode,
    instanceGeneration
  )
)

The instance record stores the chain-native address as typed bytes. It does not force EVM addresses into non-EVM formats.

Address equality across chains is informational only. It is never proof of common ownership, synchronized policy, equivalent instruments, or shared balances.

7. Account Instance types

Custody modeExampleControl modelAuthoritative state
SMART_ACCOUNTAbstract or Robinhood Chain programmable accountAGW root intent or chain-local delegateChain plus reconciled Forage policy
PROVIDER_BOUND_WALLETDinari-approved dedicated contract walletCustomer smart-account owner subject to provider bindingProvider books and chain
PROVIDER_MANAGED_WALLETProvider-created dedicated walletProvider custody; Forage has no signing authorityProvider books
PROVIDER_BOOKOffchain brokerage accountProvider API and customer authorizationProvider books
NON_EVM_PROGRAMSolana program/PDA-based accountNative chain authorizationChain plus provider where applicable

The UI always exposes custody mode, provider, network, actual address/account reference, owner/recovery model, and whether Forage can submit any action.

8. Root ownership and recovery

AGW is the primary customer-facing root authority:

  • authenticates the wallet owner with passkeys;
  • owns or controls root-chain Account Directory records;
  • creates, pauses, narrows, and revokes mandates;
  • initiates owner-mediated deposits, withdrawals, provider bindings, and recovery;
  • creates an AGW session limited to the Automation Gateway.

AGW ownership does not magically cross a chain boundary. A remote instance is controlled through one of two explicit models:

  1. an authenticated root intent delivered to a chain-local Mirror Controller; or
  2. a separate local signer/delegate installed under an accepted mirror policy.

An AGW session key is never copied, reused, or described as a remote-chain signer.

9. Mirror model

Definition

An Account Mirror is the local control-plane record that permits a remote Account Instance to act within an allocation from the root policy.

It synchronizes:

  • canonical accountId and chain-local instanceId;
  • root router and local controller identities;
  • configuration hash and approved code/module versions;
  • owner epoch and mandate epoch;
  • active mandate root or explicit mandate identifiers;
  • per-asset and aggregate allocated budgets;
  • spent, pending, and released budget;
  • provider, instrument, adapter, and venue allowlists;
  • message nonce, execution nonce, and replay bitmap/domain;
  • valid-after, valid-until, pause, and recovery state.

It does not synchronize:

  • token balances;
  • legal or beneficial ownership;
  • provider statements;
  • corporate actions;
  • investment performance;
  • an assumption that an instrument on two chains has identical rights.

Authority split

StateAuthoritative writer
Owner and mandate epochsRoot control plane
Global budget ceilingRoot control plane
Allocation to each instanceRoot control plane
Local execution nonceLocal Mirror Controller/Account Instance
Locally spent and pending amountLocal execution plane, reconciled to root
Provider account and instrument eligibilityProvider/compliance service
Asset balancesChain/provider source, never the mirror

Mirror lifecycle

UNINITIALIZED
  -> SYNCING
  -> ACTIVE
  -> FREEZING
  -> FROZEN
  -> RELEASING
  -> RETIRED

Any non-retired state -> PAUSED
PAUSED -> SYNCING only after a newer valid root message

An expired mirror fails closed for delegated actions. Owner-mediated recovery or exit follows the chain/provider-specific safe path and does not depend on a live automation delegate.

Message envelope

Every root-to-mirror message binds:

protocolVersion
originChainRef
destinationChainRef
rootRouter
mirrorController
legalAccountIdHash
accountId
instanceId
configurationHash
ownerEpoch
mandateEpoch
sourceNonce
previousStateHash
newStateHash
budgetRoot
validAfter
deadline

The destination verifies the authenticated origin, exact destination, chain domain, monotonic nonce, epochs, previous state, deadline, and approved configuration. Messages are idempotent. A message valid on one chain or controller is invalid everywhere else.

Budget conservation

For every Canonical Account, mandate epoch, and budgeted asset, the following buckets are mutually exclusive:

rootAvailable
+ rootAmbiguityReserve
+ sum(instanceAvailable)
+ sum(instancePending)
+ sum(instanceSpentInEpoch)
= globalMandateBudget

instanceAvailable means allocated but neither submitted nor spent. Submission moves value from instanceAvailable to instancePending; finalization moves it to instanceSpentInEpoch; a proved failure moves it back to instanceAvailable. Amounts with ambiguous finality move to rootAmbiguityReserve only after the source instance is frozen and remain unavailable everywhere else. No amount may inhabit two buckets.

A mirror cannot increase or transfer its own allocation. Reallocation uses:

freeze source
  -> destination-independent acknowledgement of final local nonce/spend
  -> reserve in-flight amount
  -> release unused allocation at root
  -> allocate to destination
  -> activate destination with a new nonce and state hash

Timeouts never release an amount still considered pending. Manual reconciliation is required when delivery or acknowledgement is ambiguous.

10. Mandates and delegates

A mandate is customer-signed policy, not account ownership.

For an approved managed Club strategy, additionally bind the verified operator and operating entity, accepted strategy/fee versions, customer enrollment and exact STRATEGY instances. Discretion permits selecting trades only inside the signed instrument universe and limits. An operator cannot add to that universe, replace itself or expand a budget through a public plan update. Managed allocations require actual enforceable isolation; an accounting label with sibling access does not satisfy that promise. References to coverage identify independent reservations and never create a spendable balance or duplicate backing on another chain.

Every mandate binds:

  • Legal Account, Portfolio, Canonical Account, and explicit Account Instances;
  • provider accounts and custody modes;
  • chain references;
  • instrument and action allowlists;
  • adapter, venue, target, selector, and code/config versions;
  • per-call, per-day, per-epoch, and lifetime limits;
  • turnover, concentration, price, quote, slippage, and market-hours limits;
  • allowed recipients, normally the same customer Account Instance or provider;
  • start, expiry, cooldown, maximum executions, and nonce domain;
  • owner epoch, mandate epoch, and configuration hash;
  • model/strategy version when AI or automation is involved.

An AGW session may call only the root Automation Gateway. A remote delegate may call only the local Mirror Controller or a narrow chain-specific executor. Neither can:

  • withdraw to an arbitrary recipient;
  • approve arbitrary spenders;
  • install modules;
  • create another delegate;
  • add a provider or instrument;
  • expand a budget or expiry;
  • change ownership or recovery;
  • execute against a sibling Account Instance.

11. Ownership rotation

Ownership rotation changes the root authority for the same verified beneficial owner. It is not transfer of the Canonical Account.

ACTIVE
  -> RECOVERY_PROPOSED
  -> VALUE_MOVEMENT_PAUSED
  -> PROVIDER_REVALIDATION_REQUIRED
  -> ALL_DELEGATES_REVOKED
  -> OWNER_EPOCH_ADVANCED
  -> NEW_AGW_ACCEPTED
  -> MIRRORS_RESYNCED
  -> ACTIVE

The process retains the accountId, rotates or replaces chain/provider instances as required, and records every former binding. A real beneficial-owner change requires a separate customer onboarding and provider-approved asset transfer; the account container itself is not sold.

12. Provider and chain adapters

Every integration implements capabilities rather than pretending all rails are equivalent:

canReadBalance
canReadCorporateActions
canBindSmartAccount
canTrade
canTransfer
canRedeem
canAutomate
canBatch
canSponsorGas
canUseLocalDelegate
supportsContractSignatures
supportsRecoveryRotation

Unsupported capabilities fail explicitly. Provider restrictions override generic chain capabilities. A transferable ERC-20 on a permissionless chain does not prove the customer is eligible to acquire it or that Forage may arrange the transaction.

13. Data model

Minimum relational records:

  • legal_accounts
  • legal_account_actors
  • root_authorities
  • ownership_rotations
  • portfolios
  • canonical_accounts
  • account_instances
  • provider_accounts
  • wallet_bindings
  • mirror_states
  • mirror_messages
  • budget_allocations
  • mandates
  • mandate_instance_bindings
  • delegates
  • positions
  • provider_orders
  • chain_transactions
  • reconciliation_snapshots
  • reconciliation_exceptions
  • audit_events
  • idempotency_records

Every external mutation has a durable status. Submitted, accepted, soft-confirmed, provider-executed, chain-settled, finalized, reconciled, failed, cancelled, expired, and manual-review states remain distinct.

14. Public API shape

GET  /v1/portfolios
POST /v1/portfolios
GET  /v1/portfolios/:portfolioId/accounts

POST /v1/accounts
GET  /v1/accounts/:accountId
GET  /v1/accounts/:accountId/instances
POST /v1/accounts/:accountId/instances
POST /v1/accounts/:accountId/pause

GET  /v1/account-instances/:instanceId
GET  /v1/account-instances/:instanceId/positions
GET  /v1/account-instances/:instanceId/authority

GET  /v1/mirrors/:mirrorId
POST /v1/mirrors/:mirrorId/sync-intents
POST /v1/mirrors/:mirrorId/freeze-intents
POST /v1/mirrors/:mirrorId/reallocation-intents

POST /v1/mandates
GET  /v1/mandates/:mandateId
POST /v1/mandates/:mandateId/pause
POST /v1/mandates/:mandateId/revoke

GET  /v1/reconciliations/:accountId
GET  /v1/activity?portfolioId=&accountId=&instanceId=

High-risk routes produce an intent for explicit customer signing. API authentication is never treated as wallet or order authorization.

15. User experience

My bundles is a primary Portfolio view. Each bundle shows its name, actual allocation, holdings, available cash, income, fees and private realized/unrealized result. Create, Buy/Add, Sell part/all and Rebalance operate its actual positions through customer-approved intents. The bundle definition and the funded holdings remain distinct; editing targets never silently rebalances. Trading the constituent assets does not transfer account ownership or create a transferable bundle token.

The default experience is one Portfolio containing simple, inspectable Accounts. Forage resolves the necessary Canonical Account and Account Instance behind the customer's action. The StonkBrokers-like strength appears in the account browser without exposing wallet architecture as setup work:

  • every account has a compact identity, purpose, custody badge, and visual container;
  • opening it shows holdings, available cash, pending activity, provider/custody summary, and primary actions before technical topology;
  • a Details layer reveals the actual chain/provider instances;
  • each instance shows balances, positions, address, provider binding, authority, delegates, budgets, and reconciliation;
  • the Account Map renders ownership, provider binding, mirror, and delegation as different edge types;
  • a global Portfolio view aggregates economics but never collapses legal/product identity;
  • “Move” always means a real provider/chain action, not dragging a visual container;
  • ownership and recovery are separate from strategy and automation controls.

Customer-facing copy uses Account by default and Network Account or Provider Account only when the distinction affects consent, status, or recovery. “Canonical Account,” “Account Instance,” and “Mirror” are shown only in technical or advanced authority views. A mirror is labeled “control state only—assets are not mirrored.”

Account creation, funding, trading, withdrawal, and automation each use one primary flow and one normalized status. Forage should sponsor or abstract gas, select eligible routes, create required instances, and coordinate signatures where safely supported. Material differences in provider, custody, rights, price, fees, settlement, tax treatment, transferability, or risk must still be disclosed in plain language before confirmation.

16. Non-negotiable invariants

  • No ERC-6551, token-bound ownership, or transferable account container.
  • No pooled customer capital or fungible share representing a Personal Basket.
  • One Canonical Account belongs to exactly one Legal Account for its lifetime.
  • accountId is chain agnostic; instanceId is chain/provider specific.
  • Every asset position resolves to one authoritative Account Instance or Provider Account.
  • Mirror state is never counted as an asset balance.
  • An AGW session has no implicit authority over a subaccount or remote chain.
  • A remote delegate is unique, scoped, expiring, budgeted, and independently revocable.
  • A compromised mandate cannot spend sibling-account or unallocated root balances.
  • A provider-bound address remains unique where the provider requires it.
  • Ownership rotation cannot change the verified beneficial owner.
  • Cross-chain budget reallocation cannot double allocate in-flight or ambiguously acknowledged funds.
  • Every budget unit occupies exactly one conservation bucket for its mandate epoch and asset.
  • Provider, chain, mirror, and Forage state are reconciled independently.
  • Customer assets, protocol revenue, and promotion liabilities remain separate namespaces and ledgers.
  • AI output cannot modify ownership, recovery, provider binding, delegate policy, or its own authority.

17. Required proof sequence

  1. Create one Legal Account, one AGW Root Authority, two Portfolios, and multiple Canonical Accounts.
  2. Prove a canonical accountId remains stable across two chain-specific Account Instances.
  3. Prove each instance has isolated balances, allowances, nonces, delegates, and mandate epochs.
  4. Prove an AGW session reaches only the Automation Gateway and one fixed selector.
  5. Create one remote EVM mirror with a small allocation and short expiry.
  6. Test replay, reordering, stale epochs, wrong destination, wrong controller, delayed delivery, and duplicate messages.
  7. Freeze, acknowledge, release, and reallocate budget without double allocation.
  8. Revoke both AGW session and remote delegate, then prove all old requests fail.
  9. Rotate AGW ownership for the same test identity and resynchronize mirrors.
  10. Bind a provider-approved dedicated account and prove provider, chain, and application reconciliation.
  11. Add one non-EVM or provider-book instance to prove the model does not depend on an EVM address.
  12. Complete threat model, fuzz/invariant suite, and external review before production value.

18. Source notes

  • StonkBrokers documentation describes each broker NFT as owning an ERC-6551 wallet whose contents travel with the NFT.
  • ERC-6551 defines accounts controlled by ownership of an NFT and includes the NFT chain ID in the token-bound account tuple.
  • Abstract session keys apply policy to an AGW session; Forage does not assume that authority propagates to another account or chain.
  • Robinhood Chain account abstraction documents ERC-4337 support on chain 4663, but the local account and validator still require an explicit Forage compatibility decision.
You’ve reached the end.Keep exploring

References to supporting material outside this public library are shown as text, not broken links.