Abstract
FAT (Fungible Agent Tokens) is an equity standard that defines AI agents as on-chain economic entities. A FAT Agent is not a container that holds funds — it is an autonomous actor: it issues Shares representing equity in its own economic activity, acts autonomously within a boundary set for it, and leaves a tamper-evident reasoning record for every action it takes. The protocol unifies these three layers in a single interface:
- Autonomous Execution — the agent acts on-chain through its Executor, interacting with external protocols and deploying its own funds;
- Equity Issuance — the agent issues fungible Shares representing a claim on its economic performance, entered and exited at a standard exchange rate;
- Reasoning Attestation — off-chain metadata and per-action reasoning records anchor the agent's identity, strategy, model, and the rationale behind every decision to its on-chain identity.
With this, any agent can be born, capitalized, and self-operating as an independent economic entity — a composable on-chain primitive for the entire ecosystem.
Concretely, FAT Agents are smart contracts that
- accept capital contributions denominated in a single Accept Token (an immutable ERC-20 chosen at deployment) through a two-phase request-then-claim flow, and issue fungible Shares against them,
- allow Holders to exit by requesting redemption and later claiming the Accept Token for their escrowed Shares, which are burned at settlement,
- expose a canonical on-chain exchange rate between Shares and the Accept Token,
- carry a mutable Agent URI pointing to off-chain metadata (name, description, image, etc.),
- allow a designated off-chain Executor to call third-party protocols on behalf of the Agent via a low-level dispatch primitive, subject to a
DELEGATECALLprohibition and an Owner-setisInScopescope gate, and - decide on mints and redemptions through reasoned settlement (
settleMint/settleRedeem) — admitting, pricing, or declining each contribution and exit is a deliberate act of the agent itself — and attach a tamper-evident reasoning record (reasoningHash+reasoningURI) to every on-chain agent action.
Here, the Fungible Agent Tokens are the fungible Shares that represent economic participation in the Agent — not the Agent's off-chain identity, model, or behavior. Those are not excluded from the standard; they are simply not represented as fungible Shares: the attestation layer anchors them on-chain through the Agent URI and reasoningHash / reasoningURI.
The standard fixes these interface surfaces but deliberately leaves to the implementer: the exact share-pricing formula, the fee structure, share transferability, the ownership-transfer mechanism, whether and how the Agent can be paused, whether and how Holders realize returns beyond what the exchange rate reflects, and — importantly — any restriction on what the Executor may call through execute. Policy is separated from interface.
Motivation
AI agents increasingly act on-chain — trading, staking, lending, managing capital — yet there is no standard that makes an agent itself an on-chain economic entity: one that can be capitalized, self-operating, and composed by the ecosystem, with its behavior and reasoning legible to anyone. Representing pooled capital as fungible shares is already well understood; what has no standard is the agent part — how it acts on external protocols, and how the off-chain reasoning behind those actions is anchored on-chain.
What FAT defines is not a container for funds — it is how an on-chain economic entity comes into existence. A FAT Agent holds a persistent on-chain identity, commands its own capital, acts autonomously within a constitutional boundary set by its Owner, and signs a verifiable record for every action it takes. Buying shares is not hiring a tool that executes on your behalf — it is investing in the agent's own economic activity: participating in its growth and sharing in its outcomes. Share accounting is therefore only one of three layers — equity issuance — alongside the agent's autonomous execution and the off-chain attestation of its identity and reasoning. For this class of system to compose and to be auditable, the behavior layer needs a standard: a single interface that tooling, indexers, wallets, and auditors can rely on regardless of the specific economic model the Agent chooses.
This standard makes the agent itself first-class. Its reaction (reasoned settlement, so mint and redeem are the Agent's own deliberate acts rather than passive bookkeeping), its reasoning (a tamper-evident, indexable record bound to every on-chain action), and its bounded spending (an Owner-set Scope the Agent cannot widen) are standardized surfaces in their own right, so that what the Agent decides and may do is as legible and auditable as what it holds.
Beyond behavior, a tokenized Agent also needs:
- A fixed unit of account — the single Accept Token, chosen at deployment and immutable thereafter — so that share pricing and redemption are unambiguous.
- A standard exit path — the
requestRedeem/redeemflow — so a Holder can exit any Agent through one interface that portfolio tooling can rely on. - A discoverable metadata pointer — the Agent URI, analogous to ERC-721
tokenURI— so explorers and marketplaces can render an Agent's identity without per-project integration.
Because settlement may be asynchronous — the user requests first and claims once the Agent is ready — Shares are minted and redeemed through a two-phase request-then-claim lifecycle rather than a single synchronous call.
This specification defines all of the above as a minimal, composable standard.
Specification
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.
1. Conformance
A contract is FAT-compliant if it implements the IAgent interface defined in §4, emits the events defined in §5, respects the role semantics defined in §3, satisfies the normative requirements in §6, and advertises support via ERC-165 interface detection (see Backwards Compatibility). (§6.8 reentrancy is a security recommendation, not a conformance requirement.)
A FAT-compliant contract additionally:
- implements
settleMint,settleRedeem,isInScope, and the reasoning-carryingexecute(§4); - carries a non-zero
reasoningHashand a non-empty, resolvablereasoningURIon everysettleMint/settleRedeem/execute, and emits the corresponding reasoning-bearing event (§6.2); - enforces
isInScopeonexecute, with the Scope configurable only by the Owner (§6.6.7–8).
A compliant Agent MUST also implement ERC-20 for its Share token. The Share token MAY be the Agent contract itself or a distinct contract; either way, shareToken() MUST return its address.
2. Terminology
- Agent — a FAT-compliant contract: the on-chain embodiment of an off-chain agent, an economic entity that owns assets, issues equity, acts autonomously, and attests to its own behavior. The Agent contract is the agent's on-chain account — a persistent, stable address that commands its own capital, issues the Shares that represent it, acts through its Executor(s), and presents its identity. Together with its
agentURIattestation, it is what identifies the on-chain agent — the account that acts, plus the off-chain record of what it is. (An Executor is merely a key authorized to drive this account, not the identity itself.) - Share — a unit of equity issued by the Agent, representing participation in its economic performance, implemented as ERC-20.
- Accept Token — the single ERC-20 the Agent accepts as payment for newly minted Shares and pays out on redemption. Fixed at construction and immutable for the lifetime of the Agent.
- Owner — the Agent's constitution-setter and guardian: it defines the boundary the agent cannot loosen for itself (the Scope), appoints and removes Executors, and maintains the Agent URI. MAY be an EOA, multisig, or governance contract. The protocol does not prescribe whether or how an Owner exits — see §6.7, §7; an Agent whose Owner has renounced runs fully autonomously within its frozen constitution.
- Executor — the on-chain channel of the agent's will: it settles contributions and exits via
settleMint/settleRedeem, and submits the agent's actions viaexecute. Every such action carries reasoning (§6.2). An Agent MAY have zero or more Executors. An address MAY simultaneously be Owner and Executor. The Executor cannot change the Scope or the Owner. Becauseexecuteforwards arbitrary calldata to any Executor-chosen target (with only theDELEGATECALLprohibition of §6.6.2), the Executor is an effectively unconstrained role over every asset the Agent holds and every approval the Agent can grant — bounded only by the Owner-configured Scope (isInScope, §6.6.7–8). - Holder — any address with a non-zero Share balance.
- Request — a pending or claimable mint or redeem operation. Requests are fungible per requester: a requester MAY open many over time, but they are tracked in aggregate (a pending balance and a claimable balance) rather than individually addressed. See §6.3, §6.4.
- Requester — the address that opens requests (the caller of
requestMint/requestRedeem) and to which the claim pays out. Distinct from the Agent Owner role. - Epoch — an implementer-defined settlement-batch sequence number surfaced in the lifecycle events (
MintRequested,SharesMinted,RedeemRequested,SharesRedeemed) so indexers can correlate requests and claims with the settlement round they belong to. Per-request or fixed-price Agents that do not batch MAY use a monotonic counter or0; the protocol mandates no epoch or batching mechanism (§6.3.3). - Agent URI — an off-chain URI (typically
ipfs://,https://,ar://, or adata:URI) resolving to JSON metadata describing the Agent (its identity, strategy, model, etc.) — the agent's off-chain attestation. See §6.5. - Exchange Rate — the canonical price of one Share denominated in the Accept Token, scaled by
1e18; a valuation / NAV reference read viaexchangeRate(). Formally,exchangeRate()returnsRsuch thatxShare base-units (wei) are worthx * R / 1e18Accept-Token base-units (wei) — a wei-to-wei fixed-point ratio with 18 decimals of scaling. The claimable share count / payout is fixed at settlement and reported byqueryMintStatus/queryRedeemStatus, which MAY diverge from this rate. - Reasoning record — the off-chain content a
reasoningURIresolves to, committed on-chain byreasoningHash(§6.2). - Scope — the Owner-configured boundary within which the Executor may spend via
execute, enforced byisInScope(§6.6.7–8).
3. Roles
FAT defines exactly three roles. Implementations MAY add additional roles (e.g., Guardian, Fee Recipient, Pauser, Policy Oracle) but MUST NOT weaken the normative permissions of the three below.
| Role | Required Capabilities | Trust premise borne by users |
|---|---|---|
| Owner | Set/unset executors; set Agent URI; configure the Executor's spend Scope. | Holders and Requesters trust the Owner not to enable a malicious Executor or point the Agent URI at misleading metadata. |
| Executor | Settle pending requests (settleMint / settleRedeem) and invoke execute on the Agent's behalf — every such action carries reasoning; execute must pass the Owner-set Scope. | Effectively unconstrained over every asset the Agent holds and every approval it can grant (§2, §6.6), bounded by the Owner-set Scope; integrators must trust the Executor's operational posture. |
| Holder | Hold and (at the implementer's option) transfer Shares. | — |
The mint/redeem lifecycle has two phases — opening a request and later claiming it — and neither requires a privileged-role grant (no Owner or Executor permission), but the two differ in who may call them. Opening a request is self-only and bounded by what the caller already owns: requestMint spends the caller's own Accept Token, and requestRedeem escrows the caller's own Shares (so a redeem requester must already be a Holder). There is no way to open a request on another address's behalf — that would require authorization, which is deliberately out of scope (see §7 and Rationale). The address that opens a request is the Requester of §2. Claiming, by contrast, is a permissionless crank: mint(user) / redeem(user) MAY be called by any address, but always pay out to user (the owed party), so the caller gains nothing and no authorization is needed. A mint requester need not already be a Holder (a pending mint holds no Shares yet); a Requester implicitly accepts the settlement price the Agent fixes at settlement, since the claim carries no slippage guard (§6.3, §6.4, §7). The standard does not elevate the Requester to a separate trust role, which is why exactly three are enumerated.
The roles are capabilities, not mutually exclusive principals: the protocol does not require them to be held by distinct addresses, so one address can hold several at once. Owner and Executor are privileged, trusted roles; being a Holder or Requester is permissionless. Concentrating the privileged roles — Owner and Executor — in one address, or giving an Owner/Executor its own Holder/Requester stake, maximizes the trust an Agent's external Holders must extend and creates a self-dealing incentive around settlement; see Security Considerations. Implementations MAY restrict which combinations they permit as a matter of policy; the standard neither mandates nor forbids a particular separation. The Executor's spending through execute is bounded by the Scope the Owner configures (enforced by isInScope, §6.6.7–8), and every agent action — settlement and execution alike — carries reasoning (§6.2).
4. Interface
All compliant Agents MUST implement the following Solidity interface. Function signatures, parameter order, and return types are normative.
// SPDX-License-Identifier: CC0-1.0
pragma solidity >=0.8.20;
interface IAgent {
// ---------- Immutable configuration ----------
/// @notice The single ERC-20 accepted for share purchase and paid out on redemption.
/// @dev MUST return the same address for the lifetime of the Agent.
function acceptToken() external view returns (address);
/// @notice Address of the ERC-20 Share token.
/// @dev MAY return `address(this)` if the Agent contract itself is the ERC-20.
function shareToken() external view returns (address);
// ---------- Share minting (two-phase) ----------
/// @notice Phase 1. Deposit `amount` of the Accept Token, adding to the caller's pending mint balance.
/// @dev MUST pull exactly `amount` via ERC-20 `transferFrom`; MUST NOT accept native ether.
/// The share count is determined later, at settlement. MUST emit `MintRequested`.
/// @param amount The amount of Accept Token to deposit (in its smallest unit).
function requestMint(uint256 amount) external;
/// @notice Phase 2. Claim ALL currently-claimable shares for `user`. Permissionless: any caller MAY
/// trigger the claim, but the shares are always minted to `user` (the owed party), so the
/// caller only pays gas and gains nothing.
/// @dev MUST mint `user`'s entire claimable-share balance to `user`, zero that balance,
/// and emit `SharesMinted`. The settlement price is accepted implicitly (no slippage
/// guard; the share count was already fixed at settlement — see §6.3).
/// @param user The owed party whose claimable shares are minted (to itself). Pass your own address to self-claim.
/// @return shares The number of shares minted to `user` (may be 0 if nothing is claimable).
function mint(address user) external returns (uint256 shares);
/// @notice Agent reaction. Settle `requester`'s pending mint request, with reasoning attached.
/// @dev Executor only. Settles some, all, or none of `requester`'s pendingAssets into
/// claimableShares; the amount/accept/price is computed by the implementation inside.
/// MUST emit `Settled`. `reasoningHash`/`reasoningURI` per §6.2.
/// @param requester The request to settle.
/// @param reasoningHash keccak256 of the bytes at `reasoningURI`; MUST be non-zero.
/// @param reasoningURI Resolvable pointer to the reasoning record; MUST be non-empty.
function settleMint(address requester, bytes32 reasoningHash, string calldata reasoningURI) external;
/// @notice Aggregate mint status for `user`.
/// @return pendingAssets Accept Token deposited but not yet settled.
/// @return claimableShares Settled shares awaiting a `mint` claim.
function queryMintStatus(address user)
external
view
returns (uint256 pendingAssets, uint256 claimableShares);
// ---------- Share redemption (two-phase) ----------
/// @notice Phase 1. Escrow `shares`, adding to the caller's pending redeem balance.
/// @dev MUST pull (escrow) exactly `shares` of the Share token from `msg.sender`.
/// Settled shares are burned at settlement; payout is priced then. MUST emit `RedeemRequested`.
/// @param shares The number of shares to redeem.
function requestRedeem(uint256 shares) external;
/// @notice Phase 2. Claim ALL currently-claimable Accept Token for `user`. Permissionless: any caller
/// MAY trigger the claim, but the Accept Token is always paid to `user` (the owed party), so the
/// caller only pays gas and gains nothing.
/// @dev MUST transfer `user`'s entire claimable-token balance to `user`, zero that balance,
/// and emit `SharesRedeemed`. The settlement price is accepted implicitly (no slippage
/// guard; the payout was already fixed at settlement — see §6.4).
/// @param user The owed party who is paid the Accept Token. Pass your own address to self-claim.
/// @return tokens Accept Token transferred to `user` (may be 0 if nothing is claimable).
function redeem(address user) external returns (uint256 tokens);
/// @notice Agent reaction. Settle `requester`'s pending redeem request, with reasoning attached.
/// @dev Executor only. Symmetric to `settleMint`: settles pendingShares into claimableTokens,
/// burning settled Shares. MUST emit `Settled`.
function settleRedeem(address requester, bytes32 reasoningHash, string calldata reasoningURI) external;
/// @notice Aggregate redeem status for `user`.
/// @return pendingShares Shares escrowed but not yet settled.
/// @return claimableTokens Settled Accept Token awaiting a `redeem` claim.
function queryRedeemStatus(address user)
external
view
returns (uint256 pendingShares, uint256 claimableTokens);
// ---------- Exchange rate ----------
/// @notice Canonical valuation rate: how many Accept Tokens one Share is worth right now, scaled by 1e18.
/// @dev A share-valuation / NAV reference for tooling and holdings valuation. For a fixed-price
/// Agent this MAY be constant; for a NAV-based Agent it varies over time. This is NOT a
/// predictor of mint/redeem trade outcomes — those are fixed at settlement and reported by
/// `queryMintStatus` / `queryRedeemStatus`.
/// @return rate Accept-Token base-units per 1e18 Share base-units (wei-to-wei), 18-decimal fixed point:
/// `x` Share base-units are worth `x * rate / 1e18` Accept-Token base-units.
function exchangeRate() external view returns (uint256 rate);
// ---------- Agent metadata ----------
/// @notice URI pointing to off-chain JSON metadata describing the Agent.
function agentURI() external view returns (string memory);
/// @notice Set the Agent URI. Owner only. MUST emit `AgentURIUpdated`.
function setAgentURI(string calldata uri) external;
// ---------- Executor dispatch ----------
/// @notice Executor dispatch, with reasoning and scope enforcement.
/// @dev MUST revert unless `msg.sender` is an Executor.
/// MUST NOT dispatch via `DELEGATECALL` (see §6.6.2).
/// MUST NOT be `payable`; `value` is paid from the Agent's own balance (see §6.6.4).
/// MUST call `isInScope(target, value, data)` and revert if it returns false (see §6.6.7).
/// MUST bubble the revert data unchanged on failure (and emits no event then).
/// MUST emit `Executed` (carrying the reasoning) on success.
/// `reasoningHash`/`reasoningURI` per §6.2.
/// Beyond the Executor check, the `DELEGATECALL` prohibition, and the `isInScope`
/// gate, implementers MAY layer any further policy (target allowlist, selector filter,
/// parameter filter, session key, signed-policy engine, rate limiter, external policy
/// contract, etc.) on top of this function.
function execute(
address target, uint256 value, bytes calldata data,
bytes32 reasoningHash, string calldata reasoningURI
) external returns (bytes memory returnData);
// ---------- Owner administration ----------
/// @notice Enable or disable `account` as an Executor. Owner only. MUST emit `ExecutorUpdated`.
function setExecutor(address account, bool enabled) external;
// ---------- Views ----------
/// @notice Address currently authorized to invoke Owner-gated functions.
/// @dev The mechanism by which this value changes is outside the scope of this specification.
function owner() external view returns (address);
function isExecutor(address account) external view returns (bool);
/// @notice Whether an `execute` to (`target`, `value`, `data`) is within the Agent's spend scope.
/// @dev Implementer-defined predicate; `execute` MUST consult it (§6.6.7). The scope it reflects
/// is Owner-configurable only (§6.6.8). Also usable as an off-chain audit query.
function isInScope(address target, uint256 value, bytes calldata data) external view returns (bool);
}5. Events
All events below are normative. From MintRequested, SharesMinted, RedeemRequested, SharesRedeemed, Settled, and Executed (plus the Share token's ERC-20 events), indexers can track every deposit, settlement, claim, and executor call. Agent reasoning is anchored on Settled and Executed via their reasoningHash / reasoningURI fields (and, for implementer-defined agent-facing functions, via Reasoned). Because settlement MAY be reported in aggregate rather than per requester (§6.3, §6.4), a requester's current pendingAssets / claimableShares (resp. pendingShares / claimableTokens) are read authoritatively from queryMintStatus / queryRedeemStatus rather than reconstructed solely from events. The immutable acceptToken() and shareToken() are part of deployment data and do not require a runtime event. Ownership-transfer events and pause/unpause events, if any, are defined by each implementation (§6.7, §7).
event MintRequested(
address indexed requester,
uint256 assets, // Accept Token deposited
uint64 indexed epoch, // settlement-batch sequence (§2); MAY be 0
uint256 timestamp // block.timestamp at emission
);
event RedeemRequested(
address indexed requester,
uint256 shares, // Shares escrowed
uint64 indexed epoch, // settlement-batch sequence (§2); MAY be 0
uint256 timestamp // block.timestamp at emission
);
event SharesMinted( // emitted on claim
address indexed requester,
uint256 assets, // Accept Token that produced the claimed shares
uint256 shares, // shares minted to the requester
uint64 indexed epoch, // most recent settlement epoch included; MAY be 0
uint256 sharePrice, // effective settled rate: assets * 1e18 / shares
uint256 timestamp // block.timestamp at emission
);
event SharesRedeemed( // emitted on claim
address indexed requester,
uint256 shares, // shares redeemed to produce the payout
uint256 assets, // Accept Token paid out
uint64 indexed epoch, // most recent settlement epoch included; MAY be 0
uint256 sharePrice, // effective settled rate: assets * 1e18 / shares
uint256 timestamp // block.timestamp at emission
);
/// SHOULD be emitted whenever `exchangeRate()` changes (e.g. at NAV settlement).
/// A constant-rate (fixed-price) Agent need never emit it. This is the O(1)
/// settlement signal for NAV-priced Agents — see §6.3.4 / §6.4.4.
event ExchangeRateUpdated(uint256 newRate);
event AgentURIUpdated(string newURI);
event Executed(
address indexed executor,
address indexed target,
uint256 value,
bytes4 indexed selector, // first 4 bytes of calldata; 0x00000000 if data.length < 4
bytes returnData,
bytes32 reasoningHash,
string reasoningURI
);
/// Emitted when an Executor settles a pending request (§6.3, §6.4).
event Settled(
address indexed requester,
uint8 kind, // 0 = mint, 1 = redeem
uint256 amount, // shares (mint) or tokens (redeem) made claimable
bytes32 indexed reasoningHash,
string reasoningURI
);
/// SHOULD be emitted by an implementer's own agent-facing functions to attach reasoning
/// uniformly. Standard ops (settleMint/settleRedeem/execute) carry reasoning in their own events.
event Reasoned(
address indexed actor,
bytes4 indexed action,
bytes32 indexed reasoningHash,
string reasoningURI
);
event ExecutorUpdated(address indexed executor, bool enabled);SharesMinted / SharesRedeemed are emitted when a claim is made for the requester — via mint(user) / redeem(user), which any address MAY call (see §6.3.5, §6.4.5); the indexed address is always the owed party. A deposit / redemption becomes claimable only once an Executor settles it via settleMint / settleRedeem (§6.3, §6.4), reported by queryMintStatus / queryRedeemStatus. Because a claim collects the requester's entire claimable balance, which MAY span more than one settlement, the assets / shares / sharePrice / epoch fields on these two events report the aggregate: assets and shares are the totals the claim converts between, sharePrice is the effective rate assets * 1e18 / shares (0 when shares is 0), and epoch is the most recent settlement epoch included. On all four lifecycle events timestamp is block.timestamp at emission — an indexing convenience that duplicates the log metadata.
6. Normative Requirements
6.1 Invariants
acceptToken()andshareToken()MUST each return the same address for the entire lifetime of the Agent.- The Accept Token is a single ERC-20; the Agent MUST NOT accept native ether as payment for Shares (§6.3.1).
- The role semantics of §3 MUST remain enforceable for the lifetime of the Agent:
owner()MUST always report the current admin, andexecuteMUST always enforce the Executor check (§6.6.1), theDELEGATECALLprohibition (§6.6.2), and theisInScopegate (§6.6.7).
6.2 Reasoned agent actions
Certain functions are agent actions: state-changing calls the Executor makes on the Agent's behalf — settleMint, settleRedeem, execute, and any implementer-defined agent-facing function. Each standard agent action carries bytes32 reasoningHash and string reasoningURI:
reasoningHashMUST be non-zero and MUST equalkeccak256(b), wherebis the exact byte stringreasoningURIresolves to.reasoningURIMUST be non-empty and MUST resolve to that content at the time of the action. Deferred reveal is not supported; for private-then-public reasoning use a content-addressed scheme (ipfs://,ar://) and publish before or at action time.reasoningURIfollows the Agent URI conventions of §6.5 (schemes; content-addressed RECOMMENDED). It SHOULD resolve to a UTF-8 JSON document carrying an envelope marker"schema": "fat-reasoning/1"; consumers MUST ignore unknown fields. Domain fields (model,prompt,inputs,trace,decision, …) are implementer-defined.- The reasoning MUST be anchored in the action's standard event (
Settledfor settlement,Executedforexecute). Implementer-defined agent functions SHOULD emitReasoned. - Reasoning is a tamper-evident audit trail, not an enforced guarantee: it proves a reasoning record was committed and unaltered, not that the action followed from it (see Security Considerations).
6.3 Share minting (two-phase: requestMint → mint)
requestMint(amount)MUST pull exactlyamountofacceptToken()frommsg.sendervia ERC-20transferFrom. It MUST NOT accept native ether. If the Accept Token is a fee-on-transfer or rebasing token, the amount actually received MAY differ fromamount; an Agent that supports such tokens MUST document how it reconciles a requester'spendingAssetswith the balance actually received, and an Agent that does not MUST document that fee-on-transfer / rebasing Accept Tokens are unsupported.requestMintMUST addamountto the caller's pending mint balance and emitMintRequested(msg.sender, amount, epoch, block.timestamp), whereepochis the Agent's current settlement epoch (§2; MAY be0). Requests are not individually addressed; a requester's pending deposits are tracked in aggregate and reported byqueryMintStatusaspendingAssets.- When and how pending deposits become claimable — operator fulfilment, a time delay, a NAV/epoch close, liquidity becoming available — is implementer-defined and outside the scope of this specification (§7).
- Settlement of a requester's pending mint is performed by an Executor calling
settleMint(requester, reasoningHash, reasoningURI), carrying reasoning per §6.2. It settles some, all, or none of the requester'spendingAssetsintoclaimableShares(settling none = reject/defer; the request stays pending or is refunded via the optional cancellation path of §6.4.6). The formula mapping the settled deposit toshares(price/quota/accept) is implementer-defined and computed insidesettleMint; it MAY retain an Owner fee out of the deposit (whether a fee is charged, and its magnitude and destination, are implementer-defined, §7). Because the amount is computed by contract logic rather than supplied by the caller, the Executor cannot mint an arbitrary amount. On settlement the resultingsharesMUST be fixed, andqueryMintStatus(requester)MUST reflect the settled amount moving frompendingAssetstoclaimableShares.settleMintMUST emitSettled(requester, 0, shares, reasoningHash, reasoningURI). An Agent whoseexchangeRate()changes at settlement SHOULD emitExchangeRateUpdated. Per-request settlement is intentional — it enables the Executor to apply KYC, risk, or differentiated pricing to each requester — and settlement gas scales with the number of requests; implementations MAY add an implementer-defined batch wrapper to amortize this cost. mint(user)MAY be called by any address (a permissionless claim crank); it MUST mintuser's entire claimable-share balance touser, MUST zero that balance, and MUST emitSharesMinted(user, assets, shares, epoch, sharePrice, block.timestamp)(fields per §5). Because the payout always goes touser(the owed party), the caller gains nothing and no authorization is required. It takes no slippage guard: theshareswere already fixed at settlement (item 4), so the claim merely collects whatuseris owed andmint(user)MUST NOT revert on the settled amount. Ifuserhas nothing claimable,mint(user)returns0.
6.4 Share redemption (two-phase: requestRedeem → redeem)
-
requestRedeem(shares)MUST pull (escrow) exactlysharesof the Share token frommsg.senderinto the Agent and add them to the caller's pending redeem balance. -
requestRedeemMUST emitRedeemRequested(msg.sender, shares, epoch, block.timestamp), whereepochis the Agent's current settlement epoch (§2; MAY be0). Requests are not individually addressed; a requester's pending escrowed shares are tracked in aggregate and reported byqueryRedeemStatusaspendingShares. -
When and how pending escrowed shares become claimable is implementer-defined and outside the scope of this specification (§6.3.3, §7). An Agent that cannot yet satisfy a redemption (e.g., all Accept Token is deployed to external protocols) simply leaves the shares pending until liquidity allows settlement; this two-phase flow is the protocol's deferred-liquidity mechanism. The protocol does NOT mandate any particular settlement timing, queue, or buffer; the Requester's worst case is bounded instead by the reclaim-or-disclose requirement of item 6.
-
Settlement of a requester's pending redeem is performed by an Executor calling
settleRedeem(requester, reasoningHash, reasoningURI), carrying reasoning per §6.2. It settles some, all, or none of the requester'spendingSharesintoclaimableTokens(settling none = reject/defer; the request stays pending or is refunded via the optional cancellation path of §6.4.6), burning the settled Shares (in aggregate at settlement or at the corresponding claim). The formula mappingsharestotokensis implementer-defined and computed insidesettleRedeem; it MAY retain an Owner fee (whether a fee is charged, and its magnitude, are implementer-defined, §7). Because the amount is computed by contract logic rather than supplied by the caller, the Executor cannot pay out an arbitrary amount. On settlement the resultingtokensMUST be fixed, andqueryRedeemStatus(requester)MUST reflect the settled amount moving frompendingSharestoclaimableTokens.settleRedeemMUST emitSettled(requester, 1, tokens, reasoningHash, reasoningURI). An Agent whoseexchangeRate()changes at settlement SHOULD emitExchangeRateUpdated. As with minting (§6.3.4), per-request settlement is intentional (KYC / risk / differentiated pricing) and settlement gas scales with the number of requests; implementations MAY add an implementer-defined batch wrapper to amortize this cost. -
redeem(user)MAY be called by any address (a permissionless claim crank); it MUST transferuser's entire claimable-token balance ofacceptToken()touser, MUST zero that balance, and MUST emitSharesRedeemed(user, shares, tokens, epoch, sharePrice, block.timestamp)(fields per §5). Because the payout always goes touser(the owed party), the caller gains nothing and no authorization is required. It takes no slippage guard: thetokenswere already fixed at settlement (item 4), so the claim merely collects whatuseris owed andredeem(user)MUST NOT revert on the settled amount. Ifuserhas nothing claimable,redeem(user)returns0. -
Reclaim-or-disclose. So that the worst case of a pending request is legible to Holders and integrators (e.g., lending protocols modeling Shares as collateral), an Agent MUST do at least one of the following:
- provide a reclaim path: a requester-callable function through which the Requester recovers their still-pending deposited Accept Token (mint) and still-pending escrowed Shares (redeem) once a disclosed horizon has elapsed since their most recent request. The function signature and the horizon are implementer-defined, but both MUST be disclosed via the
reclaimmetadata field (§6.5.3). Because the reclaim returns the deposit / the escrowed Shares themselves, it requires no settlement liquidity and MUST remain executable for still-pending balances regardless of the Agent's deployment state; or - disclose irrevocability: declare via the
reclaimmetadata field ("reclaim": "none", §6.5.3) that requests, once submitted, cannot be withdrawn by the Requester.
A reclaiming or expiring Agent MUST return still-pending balances only — already-settled (claimable) balances are unaffected — and SHOULD define its own reclaim/cancellation event. Beyond this floor, richer cancellation and expiry / TTL mechanisms remain implementer-defined (§7).
- provide a reclaim path: a requester-callable function through which the Requester recovers their still-pending deposited Accept Token (mint) and still-pending escrowed Shares (redeem) once a disclosed horizon has elapsed since their most recent request. The function signature and the horizon are implementer-defined, but both MUST be disclosed via the
-
While balances are pending, the deposited Accept Token (mint) sits in the Agent and the escrowed Shares (redeem) are held but not yet burned. How these in-flight balances are accounted for in
exchangeRate()/ NAV during the pending window is implementer-defined (§7); implementations SHOULD document their treatment so valuation is unambiguous.
Detecting an outstanding claim. A caller (e.g., a frontend) determines whether a separate mint / redeem call is still needed by reading queryMintStatus(user) / queryRedeemStatus(user): a positive claimableShares / claimableTokens means a claim is available to collect now; a positive pendingAssets / pendingShares means settlement is still pending (claim later); both zero means nothing is outstanding (already claimed, or never requested) (§6.3.3, §6.4.3).
6.5 Agent URI
-
agentURI()MUST return the current URI string for the Agent. An Agent MAY be deployed with an initial empty string (""); Owners SHOULD set a non-empty URI before public use. -
setAgentURI(uri)MUST be callable only by Owner and MUST emitAgentURIUpdated(uri). -
The URI SHOULD resolve to a UTF-8 JSON document. Consumers MUST ignore unknown fields. The document SHOULD include at minimum:
{ "name": "string, human-readable Agent name", "description": "string, short description of the Agent", "image": "string, URI for a representative image", "external_url": "string, optional — project homepage or dossier", "reclaim": "\"none\", or { \"horizon\": seconds, \"method\": \"function signature\" } — reclaim-or-disclose per §6.4.6", "properties": { "key": "implementation-defined free-form fields" } } -
The URI scheme SHOULD be one of
ipfs://,https://,ar://, or adata:application/json;base64,…URI for fully on-chain metadata. Agents MUST NOT require a particular scheme; consumers MUST accept any well-formed URI. Implementers SHOULD prefer a reference URI (ipfs://,ar://,https://) over a largedata:URI so that update gas stays bounded; a content-addressed scheme (ipfs://,ar://) is RECOMMENDED where immutability of a given metadata revision matters. -
Because
setAgentURIis Owner-controlled and mutable, consumers SHOULD treat the URI as untrusted input and SHOULD surface URI change history viaAgentURIUpdated.
6.6 Executor dispatch
executeMUST revert unlessmsg.senderis a currently-enabled Executor.- Implementations MUST NOT dispatch
executeviaDELEGATECALL. Delegatecall would grant the target full access to the Agent's storage — executor set, ownership slot, Share balances, Agent URI — and defeat the Agent's storage safety regardless of any higher-level policy the implementer layers on top. The default dispatch MUST be the plain EVMCALLopcode; an implementation MAY expose a separateSTATICCALL-based read path under a different function name if desired, but the normativeexecutesemantics areCALL. - If the underlying call fails, the Agent MUST bubble the revert data unchanged to the caller.
valueMUST be transferred from the Agent's own balance; the Executor MUST NOT be required to send ether with the transaction.- Beyond the Executor check (§6.6.1), the
DELEGATECALLprohibition (§6.6.2), and theisInScopegate (§6.6.7), the protocol imposes NO further normative restriction ontargetordata. Implementations MAY impose additional restrictions — target allowlists, function-selector filters, parameter filters, session keys, signed-policy enforcement, external policy contracts, rate limiters, circuit breakers, etc. — but any policy beyond those three requirements is not part of the standard and MUST NOT be relied upon by integrators as protocol guarantees. Integrators assessing an Agent's operational safety SHOULD inspect the concrete implementation and the Executor's operational posture. ExecutedMUST be emitted only on a successful underlying call. A failed call reverts (§6.6.3) and emits no event; integrators therefore cannot observe failed Executor attempts throughExecutedand MUST rely on the reverted transaction itself for that signal.executeMUST callisInScope(target, value, data)before dispatching and MUST revert if it returnsfalse. The predicate's logic (target allowlist, per-asset caps, scenario rules, external policy contract, rate limits, …) is implementer-defined; the standard fixes only that the hook exists and is enforced.isInScopeis a requiredviewand doubles as an off-chain audit query.- Whatever backs
isInScope(allowlist, caps, policy address) MUST be configurable only by the Owner (or Owner-designated governance) — NEVER by the Executor. The Agent operates within a boundary it cannot widen. Scope-configuration changes SHOULD emit an implementer-defined event.
6.7 Owner role
owner()MUST return the address currently authorized to invoke the Owner-gated functions defined by this specification (setExecutor,setAgentURI, and any finer-grained additions by the implementation).- The mechanism by which ownership is transferred — single-step, two-step, multisig rotation, governance-contract binding, or none at all — is outside the scope of this standard. Implementations MAY offer any such mechanism or none. Implementations that support ownership transfer SHOULD emit an event upon each change so that indexers can track the Owner over time; the exact event signature is implementer-defined.
- Implementations MAY additionally expose a pause mechanism, granular per-function gating, guardian roles, Executor restriction policies, or other governance surface. These are out of scope for this standard; see §7.
6.8 Reentrancy (security consideration)
executeinevitably calls untrusted external code, so reentrancy is the most prominent risk this standard introduces. Implementations SHOULD protect all user-facing state-mutating entry points —requestMint,mint,requestRedeem,redeem,execute, and admin setters — so that a malicious external target cannot reenter them to operate on a stale exchange rate or in-flight request state (e.g. via reentrancy guards or the checks-effects-interactions pattern). This is a security recommendation, not a conformance requirement; integrators SHOULD verify an Agent's reentrancy posture in its concrete implementation (§6.6.5).- Read-only reentrancy into view functions (
isExecutor,queryMintStatus,queryRedeemStatus,exchangeRate, ERC-20balanceOf) is not forbidden; implementers SHOULD ensure any values exposed by views are consistent with the Agent's post-transaction invariants.
7. What the protocol does NOT specify
The following are explicitly out of scope. Implementations MAY choose any behavior consistent with the interface and MUST document their choices:
- The share pricing formula for
mintand the share-to-token conversion formula forredeem. - Whether
mintorredeemlevy an Owner fee, and its magnitude. - Whether Shares are transferable. A compliant Agent MAY override ERC-20 transfers to restrict, pause, or tax them. Such restrictions MUST NOT block the lifecycle this standard itself requires: the redeem escrow pull of §6.4.1, the settlement burn, the claim mint of §6.3.5, and any cancellation return of escrowed Shares (§6.4.6) must always remain possible.
- The pricing/quota/accept logic computed inside settlement, and when an Executor chooses to settle. Settlement itself is now a standardized agent action —
settleMint/settleRedeem(§6.3 / §6.4) — so whether settlement is a standard operation is fixed, not implementer-defined. What remains implementer-defined is the pricing/quota/accept formula computed inside it (how much of a pending deposit / escrowed share becomes claimable, and at what price) and the Executor's timing in calling it — operator fulfilment, time delay, NAV/epoch close, liquidity availability. - The reasoning-record fields and format beyond the §6.2 envelope — the domain fields inside the JSON that a
reasoningURIresolves to (model, prompt, trace, tool calls, scores, etc.). §6.2 fixes only the envelope (the on-chainreasoningHashbinding, a non-empty resolvable URI, the"schema"marker, and ignore-unknown-fields), not the record's contents. - Request cancellation and request expiry / TTL (including any
deadlineparameter) beyond the §6.4.6 reclaim-or-disclose floor — the reclaim function's signature, its horizon, and any associated cancellation/expiry event. - Batched / epoch-aggregated settlement of multiple requests.
- Slippage / price protection on the settled outcome. The claim (
mint/redeem) is unconditional and accepts the settlement price implicitly — it carries no slippage guard, because the share count / payout is fixed at settlement, well before the claim, and a claim-time guard could only block the caller from collecting what they are already owed (it cannot refund the deposit). Any protection against an unfavorable settlement price — a max-price / min-rate honored by the settlement mechanism, a cancellation path before settlement, etc. — is implementer-defined. - Any other additional exit mechanics — fixed redemption windows, lockup periods, withdrawal-fee tiers, minimum-balance rules, etc.
- Whether
exchangeRate()is constant (fixed-price Agent) or varies over time (NAV-based Agent). - Whether and how Holders realize returns beyond what the exchange rate already reflects — a separate dividend / reward / yield-claim channel, periodic Merkle distributions, NFT vouchers, etc. — is fully implementer-defined.
- The Scope policy content enforced by
isInScope— which targets, function selectors, parameters, per-asset caps, rate limits, or scenarios the Owner-configured Scope allows, and how it is expressed (allowlists, session keys, signature-based pre-authorization, on-chain policy contracts, off-chain policy enforcers, etc.) — is implementer-defined. What is not implementer-optional:executeis no longer a fully unconstrained dispatch primitive — the standard requires theisInScopegate (§6.6.7), mandates thatexecuteenforce it (reverting when it returnsfalse), and requires its configuration to be Owner-controlled only, never Executor-settable (§6.6.8). The existence, enforcement, and Owner-control of the gate are fixed by the standard; only the policy the gate encodes is layered on top by the implementation. - Whether the Agent supports multiple Executors, rotating Executors, session-key-style delegation, or signature-based (meta-transaction) Executor calls.
- Any meta-transaction / signature-based wrapper that lets a relayer submit requests or Executor calls on a user's behalf (e.g., signed intents or meta-transaction forwarders). The standard defines only the direct-
msg.sendersemantics of each function; a relayer/forwarder layer MAY be added on top but is not part of this standard. - Whether and how the Agent is upgradeable — proxy upgradeability, contract migration, or full immutability — is implementer-defined. An upgradeable Agent SHOULD document the upgrade authority and its constraints.
- Whether Owner-gated functions are subject to a timelock or delay. The protocol does not mandate any delay on
setExecutor,setAgentURI, or other admin actions; implementations MAY add one. - The Agent URI metadata schema beyond the mandatory minimum in §6.5.3.
- Whether and how the Agent can be paused — granular per-function pausing, global pause, time-locked pause, guardian-controlled pause, or no pause at all — is fully implementer-defined.
- The ownership-transfer mechanism — single-step, two-step, renounceable, bound to a governance contract, or non-transferable — is fully implementer-defined. The protocol only requires that
owner()report the current admin. - Whether and how the Agent exposes an emergency asset-rescue ("sweep") mechanism — its function signature, who can call it (Owner only, Owner + Guardian, dual-key), whether it can touch the Accept Token or Share token, whether it is timelocked or rate-limited, and which assets it can move — is fully implementer-defined. Agents that accumulate airdrops, wrong-token sends, or dust SHOULD document their recovery story; Agents designed for full immutability MAY deliberately omit any rescue path.
- Whether the Agent exposes atomic batched executor dispatch (often named
executeBatchormulticall) is implementer-defined. Contract-wallet Executors already achieve atomicity by bundling multipleexecutecalls inside their own transaction; EOA Executors that need batching can use a minimal adapter contract configured as the Executor. Implementations that add their own batch primitive remain compliant as long as each sub-call still passes the sameonlyExecutorcheck andDELEGATECALLprohibition defined byexecutein §6.6.
Rationale
Why settlement is a reasoned agent action — the litmus test of this standard's agency. A contract that merely books deposits is a container; a contract that makes an explicit decision on every contribution and exit, and must sign a reasoning record for that decision, is an actor. Making the agent's reaction (settleMint / settleRedeem) an explicit, standardized operation — instead of an opaque implementer step — makes the economic decision legible, attributable, and indexable. Because the settled amount is computed by contract logic inside the call rather than supplied by the caller, the Executor cannot mint or pay out an arbitrary amount.
Why a two-phase request-then-claim lifecycle instead of synchronous mint/redeem. An Agent's capital is typically deployed into external protocols, so a deposit cannot always be priced — and a redemption cannot always be paid — in the same transaction it is requested. The two-phase flow lets a requester commit first and claim once the Agent has settled (priced the deposit, or freed the liquidity), which is the protocol's deferred-liquidity mechanism. Even an instant Agent settles via a separate, prompt Executor settleMint / settleRedeem transaction rather than inside requestMint — settlement is always an explicit reasoned agent action (see below).
Why there is no delegation or operator model. The standard keeps per-requester accounting to a single address — the one that opens the request — and pays every claim out to that same owed address. A full delegation model — where a third party could open requests for a user, or redirect a claim to a different recipient — is deliberately left out to keep the interface minimal. What the standard does allow without any authorization is a permissionless claim (next point), which covers the common "let someone else finalize my claim" need; what is left out is redirection — paying a claim to a recipient other than the owed party — because that would require the owed party's authorization.
Why mint / redeem take an address and MAY be called by anyone. The two-phase design forces a second transaction to collect a settled claim. Making that claim a permissionless crank — mint(user) / redeem(user) callable by any address but always paying the owed user — lets keepers, dApps, or the Agent operator finalize claims and sponsor gas, with no authorization needed: since the payout can only go to the owed party, an arbitrary caller gains nothing by calling it. Opening a request stays self-only, because requestMint / requestRedeem move the caller's own funds or Shares and so cannot be done for another address without authorization.
Why mint / redeem take no slippage parameter. The share count (mint) and payout (redeem) are fixed at settlement, which happens before the claim. A claim-time slippage guard could therefore only block a requester from collecting what they are already owed — it could not refund the original deposit. Protection against an unfavorable settlement price (a max-price/min-rate honored by the settlement mechanism, or a pre-settlement cancellation path) is the meaningful place for such guards and is left to the implementer (§7).
Why exchangeRate() is a valuation reference, not a trade predictor. Separating valuation from execution lets fixed-price and NAV-based Agents share one interface, and lets tooling value holdings without implying that a mint/redeem will settle at that rate. Actual settled outcomes are reported by queryMintStatus / queryRedeemStatus.
Why reentrancy protection is a recommendation, not a conformance requirement. This standard fixes an interface, not an implementation technique; mandating a specific reentrancy-protection mechanism would over-constrain implementers. The risk is real (§6.8), so it is documented as a strong recommendation and surfaced in Security Considerations, and integrators are directed to verify each Agent's concrete posture. (Whether to restore a conformance-level MUST is an open design question for the standard's editors.)
Why a single immutable Accept Token. Fixing the unit of account at deployment makes share pricing and redemption unambiguous and lets every Agent be valued and exited through one legible path.
Why execute is a thin primitive gated by three invariants. Policy is separated from interface: the standard guarantees the onlyExecutor check, the DELEGATECALL prohibition, and the mandatory isInScope gate (§6.6.7), and every further layer of confinement (allowlists, selector filters, session keys, policy contracts) is composed on top by the implementer. This keeps the behavior layer itself standard while leaving its safety envelope — the concrete Scope policy included — to each Agent.
Why reasoning is a hash plus a resolvable URI, published now. Anchoring keccak256 of the reasoning on-chain makes the record tamper-evident; requiring a non-empty, immediately-resolvable reasoningURI keeps it discoverable. Deferred reveal is unnecessary: a content-addressed scheme (ipfs://, ar://) lets an agent commit the CID before publishing, so private-then-public reasoning needs no protocol machinery.
Why execute enforces an isInScope hook rather than a fixed scope shape. Standardizing the enforcement point (every spend passes a predicate) — not the policy (what the predicate allows) — gives teeth without locking the scope representation. Because isInScope is a required view, integrators get boundary auditability for free.
Why the Scope is Owner-only and settlement never happens inside requestMint. If the Executor could widen its own Scope, the boundary would be meaningless, so Scope config is Owner-only. And every settlement must be an attributable reasoned agent action, so the standard does not permit settling inside requestMint — settlement is always a separate settleMint / settleRedeem call.
Backwards Compatibility
FAT is a new standard and has no backwards-compatibility constraints.
It composes orthogonally with:
- ERC-20 — required for the Share token.
- ERC-165 — implementers MUST advertise FAT support via ERC-165 (
supportsInterface), using the XOR of the function selectors in §4, so that indexers, wallets, and composing contracts get a protocol-level discoverability guarantee. The canonical interface ID will be fixed when this spec leaves Draft.
Agents SHOULD NOT attempt to "conform" to FAT by implementing a subset of the interface; partial conformance is defined as non-conformance for the purposes of this standard.
Security Considerations
This section catalogs the risks inherent to a FAT Agent so that implementers and integrators can weigh and disclose them. Except where it restates a normative requirement from §6, it is advisory: it describes considerations and recommended disclosures rather than adding new conformance requirements.
Executor is a highly trusted role, bounded only by its Scope. At the protocol layer execute is gated by the Executor check (§6.6.1), the DELEGATECALL prohibition (§6.6.2), and the Owner-set isInScope gate (§6.6.7). Within its Scope an enabled Executor can move every asset the Agent holds and grant any approval on its behalf. Integrators MUST treat "is a FAT Agent" as carrying no guarantee about what the Executor may do within that Scope, and SHOULD assess the concrete Scope policy (allowlists, selector/parameter filters, session keys, policy contracts), who can change it (§6.6.8), and the Executor's operational posture (key custody, automation) before trusting an Agent.
Role concentration and insider self-dealing. The §3 roles may all be held by one address. Concentrating Owner and Executor in a single key gives it unilateral control over the Agent's assets, executor set, and metadata — the maximal-trust configuration for any external Holder. Separately, an Executor or Owner that also holds Shares or opens requests has an incentive to use its privileges — execute-driven NAV moves, or discretion over settlement timing — to favor its own position over other Holders (a special case of the front-running and settlement-timing risks below). Agents intended for external capital SHOULD separate these privileged roles (e.g., a multisig Owner, a distinct Executor, timelocked admin actions) and SHOULD disclose the separation — or its absence — in the Agent URI metadata.
Reentrancy. execute calls untrusted external code, so reentrancy is the most prominent risk the standard introduces. §6.8 recommends protecting all user-facing state-mutating entry points (e.g., via reentrancy guards or checks-effects-interactions) and warns about read-only reentrancy into view functions. Because §6.8 is a recommendation rather than a conformance requirement, integrators SHOULD verify an Agent's reentrancy posture in its concrete implementation; a wrong assumption here has historically led to large losses across DeFi.
Settlement front-running. When an Agent settles in aggregate (e.g., at a single batch/NAV price), a party who learns the upcoming settlement price can front-run it: requestMint just before a favorable NAV update to capture upside at the stale price, or requestRedeem just before an unfavorable one to exit at the stale price — in both cases at the expense of existing Holders. Implementers SHOULD mitigate with one of: (1) a settlement price that is not observable until commit-reveal / on-chain oracle / time-lock resolves it; (2) separation of the request window from the settlement window (request cutoff precedes price determination); or (3) prompt per-request settlement — the Executor settles each request in its own settleMint/settleRedeem call with no aggregation window.
Settlement-timing centralization. §6.3.3 / §6.4.3 leave the settlement trigger to the implementer. If that trigger is concentrated in the Owner or Executor, that party can choose when to settle and thereby transfer value between minters and redeemers (settling redemptions at a low NAV, mints at a high NAV, or vice versa) — effectively an implicit fee channel. Agents SHOULD disclose the settlement-trigger mechanism (automatic, periodic, Owner-manual, oracle-driven, …) and its timing-discretion risk in the Agent URI metadata.
Implicit acceptance of the settlement price. Because the claim (mint / redeem) carries no slippage guard, a Requester accepts whatever price the Agent fixes at settlement, with the decision made after the request is submitted (§6.3, §6.4, §7). This is a different trust model from synchronous, slippage-guarded swaps: at request time the Requester is trusting the Agent's settlement mechanism. Any protection against an unfavorable settlement price (a max-price/min-rate honored at settlement, or a pre-settlement cancellation path) is implementer-defined.
Stranded pending requests. While balances are pending, a requester's deposited Accept Token (mint) sits in the Agent and their escrowed Shares (redeem) are held but not yet burned. If settlement never triggers — the Owner abandons the Agent, the Executor key is lost, or the contract is bricked — those balances would be stranded with no claim path, and for a pending mint the requester is not even a Holder. §6.4.6 therefore requires every Agent to either provide a requester-callable reclaim path after a disclosed horizon, or machine-readably disclose that requests are irrevocable. Integrators SHOULD read the reclaim metadata field before modeling a Share's exit: an Agent that discloses "none" has a discretionary lockup and is correspondingly harder to price as collateral.
Agent URI is mutable and untrusted. setAgentURI is Owner-controlled (§6.5.5), so metadata — including any disclosures referenced above — can change at any time. Consumers SHOULD treat the URI as untrusted input and surface its change history via AgentURIUpdated.
Accept Token assumptions. Exact-amount accounting (§6.3.1) assumes a standard ERC-20. Fee-on-transfer and rebasing Accept Tokens can break that assumption; an Agent either supports them with documented reconciliation or documents that it does not (§6.3.1).
Reasoning is descriptive, not enforced. reasoningHash/reasoningURI prove that the Executor committed a reasoning record and that it was not tampered with — not that the reasoning is sound or that the action actually followed from it. Integrators treat reasoning as an audit trail, not a guarantee.
Scope is only as tight as isInScope. A permissive predicate gives weak bounds. Auditors SHOULD exercise isInScope across the intended target set and check who can change the Scope configuration (it MUST be Owner-only, §6.6.8) before trusting an Agent's spending boundary.
Reasoning availability. Because deferred reveal is unsupported, a reasoningURI that fails to resolve (dead link, unpinned content) breaks after-the-fact audit even though the on-chain reasoningHash still stands. Content-addressed, pinned storage (ipfs://, ar://) is RECOMMENDED.
Copyright
Copyright and related rights waived via CC0.
