ERC-8192: Mandated Execution for Tokenized Vaults

Hi everyone,

I’d like to start an initial discussion for a new ERC proposal tentatively titled “Mandated Execution for Tokenized Vaults”.

Process note: ERC proposals are submitted to ethereum/ERCs (not ethereum/EIPs). This EthMagicians thread is the recommended first step and will be linked from the draft via discussions-to.

Links

Summary (TL;DR)

This ERC defines a minimal, interoperable interface for delegated strategy execution on ERC-4626 tokenized vaults, enabling external executors (agents/solvers) to submit on-chain executions without custody of the authority’s private key, while the vault enforces hard risk constraints on-chain.

Core idea:

  • The vault exposes a standardized execute(…) entrypoint.

  • An Authority signs an EIP-712 Mandate defining:

an adapter allowlist commitment (Merkle root over (adapter address, adapter codehash)),

a max single-execution drawdown bound,

a max epoch cumulative drawdown bound (high-water mark),

optional binding to an intended action batch (payloadDigest),

hash-committed extensions (extensionsHash).

  • The Executor submits execute(mandate, actions, signature, adapterProofs, extensions).

  • The vault executes adapter calls via CALL only and then enforces a circuit breaker by comparing pre/post totalAssets() (ERC-4626).

Minimal Interface (Excerpt)


struct Action {

  address adapter;

  uint256 value; // MUST be 0 in Core v1

  bytes data;

}

struct Mandate {

  address executor;

  uint256 nonce;

  uint48 deadline;

  uint64 authorityEpoch;

  uint16 maxDrawdownBps;

  uint16 maxCumulativeDrawdownBps;

  bytes32 allowedAdaptersRoot;

  bytes32 payloadDigest;

  bytes32 extensionsHash;

}


function execute(

  Mandate calldata mandate,

  Action calldata actions,

  bytes calldata signature,

  bytes32 calldata adapterProofs,

  bytes calldata extensions

) external returns (uint256 preAssets, uint256 postAssets);

Design Goals / Non-Goals

Goals:

  • A minimal “thin waist” interface for vault-native delegated execution, with strong on-chain risk bounds.

  • Support both EOAs (ECDSA) and contract authorities (ERC-1271).

  • Keep the core compatible with ERC-4626 vault accounting semantics.

Non-goals (at least for Core v1):

  • Standardizing a full intent system, solver marketplace, or off-chain discovery layer.

  • Supporting arbitrary ETH value forwarding in Core (push to extensions if needed).

Motivation

DeFi is increasingly moving from “users manually call contracts” toward “agents/solvers execute strategies.” ERC-4626 standardizes pooled custody and accounting, but it does not standardize a trust-minimized way for a vault to grant bounded execution power to third-party executors.

Existing standards cover adjacent layers (delegation plumbing, intent expression, smart account execution), but there is no minimal “thin waist” interface for vault-native, risk-constrained delegated execution that:

  • keeps authority in an off-chain signature (EIP-712 / ERC-1271),

  • enforces an allowlist of callable strategy components (adapters),

  • enforces post-execution risk bounds at the vault level (via ERC-4626 accounting).

Specification Overview (Core)

Core concepts:

  • Action: a low-level call to an adapter: { adapter, value, data }.

Core forbids native value transfer: action.value MUST be 0.

Calls are executed via CALL only (no DELEGATECALL).

  • Mandate: EIP-712 signed authorization with nonce/deadline replay protection.

Supports EOA and ERC-1271 contract authorities.

Includes authorityEpoch to domain-separate signatures across authority rotations.

  • Adapter allowlist: Merkle root over (adapter, extcodehash(adapter)) so allowlisting pins runtime bytecode.

  • Circuit breaker:

Single-execution loss bound: compare preAssets = totalAssets() vs postAssets.

Cumulative loss bound: compare epochAssets (high-water mark) vs postAssets.

epochAssets updates to the maximum observed totalAssets() within the epoch.

Authority can resetEpoch() to start a fresh epoch.

  • Extensions:

Hash-committed via extensionsHash = keccak256(extensions).

Canonical encoding: extensions = abi.encode(Extension) with strictly ascending unique id.

Unknown required extensions revert; unknown optional extensions may be ignored.

Example extension: SelectorAllowlist@v1 to constrain (adapter, selector) pairs per action.

Security Model & Caveats

  • totalAssets() manipulability: this ERC’s circuit breaker assumes totalAssets() is resistant to atomic manipulation by the executor within the same transaction. Vaults whose valuation can be flash-manipulated (e.g., spot-price oracle usage) should not rely solely on this mechanism and should add additional safeguards (e.g., TWAP, oracle extension).

  • Upgradeable proxy adapters: codehash pinning does not generally pin proxy implementations; proxy-style adapters are unsafe unless additional pinning is added.

  • Reentrancy / vault busy: reference implementation uses a shared mutex to prevent reentering execute, and to prevent share mint/burn entrypoints (deposit/mint/withdraw/redeem) during execute.

  • Unbounded open mandates are forbidden: if executor == address(0) then payloadDigest must be nonzero (bind to the intended actions).

  • Audit status: The reference implementation is unaudited and provided for discussion purposes only. It has not undergone formal third-party security review. Do not use in production without independent audit.

Anticipated Questions (FAQ)

Q1: Can totalAssets() be manipulated within the same transaction, defeating the circuit breaker?

Yes, the circuit breaker is only as strong as the vault’s totalAssets() implementation. Vaults that derive valuation from spot prices (e.g., AMM reserves) are vulnerable to flash-loan manipulation within the same transaction. This is an explicit caveat in the spec (Security Model §1). The recommended mitigation is to use TWAP or oracle-based valuation, which can be enforced via extensions (e.g., an OracleValuation@v1 extension). The circuit breaker provides a meaningful bound for vaults with manipulation-resistant accounting (e.g., lending protocol aTokens, staking wrappers).

Q2: Doesn’t the codehash pinning in the adapter allowlist fail for upgradeable proxy adapters?

Correct. For ERC-1967 or similar proxies, extcodehash returns the proxy’s bytecode hash, not the implementation’s. A proxy upgrade changes behavior without changing the allowlisted codehash, so previously valid Merkle proofs remain valid post-upgrade. This is documented in this draft and in the reference implementation repo’s SECURITY.md. The recommended operational mitigation is: (1) prefer immutable adapters for long-lived mandates, (2) revoke and re-sign allowlist roots when proxy implementations change. A future extension could add implementation-level pinning (e.g., ImplementationPin@v1), but this is explicitly out of scope for Core.

Q3: Why should this be an ERC rather than an application-level convention?

Standardization at the ERC level enables a shared interface for the entire executor/agent ecosystem:

  • SDK composability: agent frameworks and solver networks can integrate with any compliant vault without per-vault adapters.

  • Audit reuse: a single audited reference implementation reduces per-deployment audit costs.

  • Tooling: block explorers, monitoring dashboards, and risk analysis tools can parse MandateExecuted events uniformly across all vaults.

  • Interoperability: depositors can evaluate vault risk constraints (drawdown bounds, adapter allowlists) through a common on-chain interface, regardless of the vault operator.

Without standardization, every vault reinvents execution delegation with incompatible interfaces, fragmenting the agent layer.

Q4: Why does Core forbid action.value != 0? Isn’t that too restrictive?

Core sets action.value == 0 as the baseline for three reasons:

  1. Accounting clarity: ERC-4626 totalAssets() tracks ERC-20 balances. Native ETH flows create accounting blind spots that are hard to reason about in the circuit breaker.

  2. Attack surface reduction: allowing arbitrary ETH forwarding via adapters opens re-entrancy and gas-griefing vectors that complicate the security model.

  3. Extension path: if a vault needs ETH flows, it can implement an extension (e.g., NativeValueForwarding@v1) with explicit opt-in. This keeps Core minimal while supporting the use case.

Feedback Requested (Open Questions)

  1. Merkle leaf encoding safety: current draft uses leaf = keccak256(abi.encode(adapter, codeHash)) (64-byte preimage). OpenZeppelin warns that 64-byte leaves can be reinterpreted as internal nodes in some constructions. Should Core:

add explicit domain separation / prefixing for leaves, or

switch to abi.encodePacked with a prefix, or

version the allowlist leaf scheme (e.g., AdapterAllowlist@v2)?

  1. Native ETH value transfer: is keeping Core as value == 0 the right baseline, pushing ETH flows into explicit extensions?

  2. Revert data size / gas griefing: should implementations cap revert-data copying (at the cost of losing full diagnostic bytes) or keep full data as in the reference implementation?

  3. Input size limits: should some limits (actions/proof depth/extensions bytes) be required vs recommended?

  4. Additional standardized extensions: should we standardize common extensions (oracle/TWAP valuation, slippage rules, adapter capability introspection) or keep Core minimal and let ecosystems define them?

Next Steps (ERCs Submission Workflow)

  1. Create EthMagicians discussion thread (this post).

  2. Publish/confirm a public reference implementation repo (spec + code + tests) so reviewers can actually read the draft.

  3. Fill remaining frontmatter in the draft (author, discussions-to, etc.).

  4. Fork ethereum/ERCs.

  5. Add erc-XXXX.md converted from the draft.

  6. Open a PR; editors assign an official number.

  7. Track status progression: Draft → Review → Last Call → Final.

Thanks in advance for feedback.

— tabilabs (tabilabs) (lancy@tabilabs.org)

1 Like

Hi Lance,

Strong proposal — this is the right abstraction layer.

One gap worth addressing in the spec : the RWA oracle
trust model. When totalAssets() is derived from off-chain
physical data (occupancy rates, energy production,
payment schedules) rather than on-chain DeFi primitives,
the circuit breaker’s guarantees depend entirely on
oracle design assumptions that the current spec leaves
implicit.

We’re implementing this pattern in production on Base L2
with physical RWA as underlying. Happy to contribute a
formal OracleValuation@v1 extension spec based on our
production experience.

Sent you a note by email as well.

— Patrick Nicolas B

1 Like

Hi everyone, a quick progress update since opening this thread.

One point we want to make more explicit up front: our intention is for this work to remain on an open ERC track and define a shared, composable base layer, rather than evolve into a protocol-specific execution framework.

That framing is shaping how we are drawing the boundaries:

  • keep Core minimal and reusable

  • validate that the model is actually integrable in real developer flows

  • push more specialized policy into extensions wherever possible

Since the initial post, we have been pushing on three related fronts in parallel: the public implementation path, the developer tooling around it, and the Core vs extension boundary.

1. Public implementation progress

We now have a public repo for the draft + implementation path here:

  • https://github.com/tabilabs/mandated-vault-factory.git

That repo packages the ERC-XXXX draft, the vault/factory implementation, and the current test surface together. The public coverage there is centered on the core execution and safety model, including:

  • EIP-712 / ERC-1271 mandate verification

  • adapter allowlist enforcement

  • single and cumulative drawdown circuit breakers

  • replay protection / nonce invalidation

  • selector allowlist extension behavior

  • execution guardrails such as action.value == 0

2. SDK / MCP progress

We also moved the SDK / MCP work into a separate public repo:

  • https://github.com/tabilabs/erc-mandated-sdk-mcp.git

The published packages are currently at 0.2.0 for both @erc-mandated/sdk and @erc-mandated/mcp.

The reason this matters in an ERC discussion is not package promotion. It is that we want to pressure-test whether the proposed execution model is actually integrable by agent apps and wallets, not just readable at the spec level. If this is going to be a credible open standard layer, it has to be usable across different applications without collapsing into app-specific conventions.

Recent work here has focused on session-style fund-and-action orchestration while keeping the signing boundary outside MCP.

3. Predict-style integration path

We have also been validating a Predict-style flow on the current SDK / MCP path: an agent detects an opportunity, the target account is underfunded, MCP primitives are used to plan / prepare the funding step, an external signer / wallet executes the actual transfer, and only then does the follow-up action proceed.

The important point is not the specific app. It is that the same path can be modeled as a generic fund-and-action abstraction, and that MCP is not the runtime executor and does not hold private keys.

In other words, the goal is not to standardize one app workflow, but to test whether a shared execution primitive can support multiple workflows without hard-coding protocol-specific behavior into Core.

4. On the OracleValuation direction

The extension discussion in this thread has been especially helpful, because it goes directly to the question of what belongs in the shared base layer versus what should remain opt-in.

The OracleValuation@v1 direction mentioned here is especially relevant because the draft already makes an explicit caveat: the circuit breaker is only meaningful if totalAssets() is manipulation-resistant over the execution window. For tokenized vaults backed by offchain assets or oracle-derived valuation, an oracle/TWAP-style extension seems like the right place to carry that logic, instead of overloading Core.

More broadly, our current bias is to keep Core minimal and reusable, and push use-case-specific policy into extensions. We think that is the healthier path if this is to become a real shared standard layer rather than a thin wrapper around one protocol family’s needs.

If finer-grained execution scoping turns out to be broadly useful beyond the current selector allowlist model, that also looks more like extension territory unless there is a strong case that it belongs in the base interface.

5. What we want feedback on next

At this point, the most useful feedback for us is:

  • whether the current Core / extension boundary is drawn in the right place

  • which extensions should be standardized first versus left ecosystem-specific

  • how much of the agent integration surface, if any, should be standardized at the ERC level versus left in the SDK / MCP layer

If useful, we can follow up in this thread with a more focused note on:

  • fund-and-action session modeling

  • why we keep MCP outside the signing boundary

  • where oracle-backed valuation and finer-grained execution scoping should live relative to Core

Thanks again for the discussion so far.