ERC-8454: AIFi Unit Framework

Status (2026-10-27): Draft; assigned ERC-8454 on 2026-10-21 (ethereum/ERCs#2095). Revision 5 in the repository and the PR (claim identity and overlap, Section 3.8; dependent evidence, Section 10.6; additivity must be established, Sections 3.8 and 10.6; confirmation bound to the relation, Sections 3.4 and 3.8; interval bounds over admissible resolutions, Sections 3.8 and 10.6; exception vectors 7 to 10). Editor review pending.

This is a draft ERC for the AIFi Unit Framework (AUF): a registry and descriptor model through which any economic object in an AI-driven economy is named, bounded, described, observed, classified and valued as one whole.

Repository (ERC text, reference implementation, schemas, vectors, worked examples): GitHub - garyyang-finchip/aifi-unit-framework: AIFi Unit Framework · GitHub

Pull request: Add ERC: AIFi Unit Framework by garyyang-finchip · Pull Request #2095 · ethereum/ERCs · GitHub (assigned ERC-8454).

Why

Agents, skills, tasks, models, compute, data and capital are increasingly issued, traded and composed on chain, and several proposals describe individual kinds of these objects, their discovery and their collective decisions. What is missing is the layer underneath: a shared answer to “what is treated as one economic whole here, what does it do economically, and what does this value figure mean?” Without it, two parties mean different things by “the same” agent business, a staking pool or a task gets one permanent label that hides its real roles, and two evaluators publish 100 and 200 without anyone knowing whether those are net equity, a daily yield, a probability or a credit grade, in which currency, as of when, for whose interest.

What the framework fixes

  1. Boundary. A Unit is a versioned Unit Definition: elements (agent, skill, task, service, model, compute, data, financial-position, nested unit), typed relations stating the rights and obligations under which each element is included (owns, licenses, reserves, revenue-share, holds, owes, commits, pledges, depends-on, exchanges, controls, contains), ports (economic interfaces), authorities and validity. boundaryHash = keccak256(JCS(definition)). Containment pins versions and is acyclic by construction. Typed acknowledgments (participation, resource use, fund operation, endorsement) separate what is claimed from what is confirmed.
  2. Characterization. Stackable roles (source, sink, conductor, damping, container, pledge, valve, control) attributed to the Unit, a port or an element under conditions; behavior attributes declared separately; behavior models (including an optional financial-circuit analysis profile with stated premises); structure profiles (simple, composite, chip); the credit application profile; explicit knowledge states.
  3. Measurement and valuation statements. Observation records reconciled per asset (missing is never zero; internal transfers are not flows), and valuation receipts that fix subject version, assessment boundary, interest and perspective, snapshot, content-addressed method, inputs and a typed result (monetary incl. negative, interval, scenario, score, grade, not-valuable, insufficient-evidence, …).

What it leaves open: the valuation functions, behavior models and credit models themselves. Methods are content-addressed descriptors with declared composability; any evaluator can plug in any method while readers still understand what a figure refers to.

Kernel

Two registries: AUFUnitRegistry (units, versions, containment, definition control, acknowledgments) and AUFAssertionRegistry (one assertion envelope for characterizations, observations, valuations and extensions; validity, revocation, supersession; EIP-712/ERC-1271 signed assertions; content-addressed descriptors). Requires only ERC-165, EIP-712 and ERC-1271. Other agent-economy proposals (skills, task tenders, agent identity, KYA, collective decisions, ARD discovery, swarms, ERC-8004) are descriptive bindings, not dependencies. A swarm describes a collaboration organization; a Unit describes an economic boundary, its roles and its measurement.

What is in the repository

  • Full ERC text passing eipw.
  • Reference contracts with 57 Foundry tests, ethers ↔ solc vector cross-checks (interface ids, canonicalization, element ids, EIP-712 struct hash, bit-exact fixed-point valuation vectors).
  • JSON Schemas for all document kinds, four method descriptors (net equity, realized net cash flow, finite-horizon DCF, conditional payoff).
  • Seven worked Units: a skill with several non-exclusive licenses, two agent sub-units sharing one license, a swarm as an economic Unit containing them, a task with escrow and conditional settlement valued for three different interests, a contingent obligation with no cash flow (negative and insufficient-evidence receipts), and a running agent packaged as a chip with a financial-circuit model statement.
  • A reference resolver (offline and on-chain) that verifies hashes, expands containment by pinned versions, flags shared elements, checks reconciliation and reports unsupported required extensions.
  • A four-phase Sepolia evidence script, rehearsed end to end on a local chain; the Sepolia run and its record follow.

Questions for reviewers

  1. Is the split between definition control, resource control, representation and cash-flow rights sufficiently explicit, or should the registry carry more than a controller address?
  2. Are the eight roles and the four behavior attributes the right v1 vocabulary, and is the credit-as-profile decision right?
  3. Is the valuation receipt’s on-chain core (method, interest, assessment boundary, snapshot, result kind, value, denomination, as-of, horizon) the right minimum for indexers, or should interval bounds or an evidence root be on chain too?
  4. Acyclic containment by version pinning versus explicit cycle detection: are there compositions this rules out that should be allowed?

Feedback welcome. The text is deliberately self-contained; bindings to the related proposals will deepen in later revisions.

1 Like

The Sepolia evidence run is done: 40 transactions, blocks 11964286 to 11964613, every receipt successful, both registries source-verified.

What is on chain: the four method descriptors, the seven worked Units (the swarm Unit 4 contains Units 2 and 3 at pinned version 1), seven controller characterizations, three observations and eleven valuation receipts signed by a separate evaluator key and relayed through assertStatementBySig, two typed acknowledgments, one superseding receipt, one revocation sent by the evaluator itself, and version 2 of Unit 1 (its version-1 receipts do not carry over). The state read back from the registries matches the published descriptors field by field.

Record: aifi-unit-framework/docs/sepolia-evidence.md at main · garyyang-finchip/aifi-unit-framework · GitHub (transactions, decoded state, how to re-run the resolver against the chain). ERC pull request: Add ERC: AIFi Unit Framework by garyyang-finchip · Pull Request #2095 · ethereum/ERCs · GitHub.

How does shared-element detection handle two different references to the same underlying economic claim?

Suppose two child Units describe the same revenue entitlement through different contracts or descriptors. Their element identifiers differ but adding both valuations would count the same cash flow twice.

Does the framework require a common identity for the underlying claim or leave that reconciliation entirely to the evaluator? If it is the latter then I’m not sure that declared method composability is enough to justify aggregation.

Could you add a worked example where the references differ and the resolver must either establish the overlap or report insufficient evidence for a combined valuation?

1 Like

How should a resolver handle a valuation whose supporting observation is later revoked? Section 6.4 defines current status per assertion, while Section 10.3 lets receipts reference observation assertion IDs. Suppose receipt V uses observation O, then O’s asserter revokes it after discovering a measurement error. V remains active and unexpired. Should the resolver flag V as relying on withdrawn evidence?

Could Section 10.6 require resolvers to show the status of referenced observations alongside the valuation? It would help to distinguish evidence withdrawn because it was wrong from an observation whose validity window simply ended after the valuation’s as-of time. A test case covering O → V → revoke(O), with expected resolver output, would make this behavior clear.

1 Like

Thanks, this is the right question, and the previous text did not answer it well enough: the resolver recognised a shared element only when the two references hashed to the same elementId, so one entitlement reached through two wrappers (a token in one child, a contract in the other) slipped through, and a method declared composable would have added it twice.

The revision is now in the repository and in PR #2095 (new Section 3.8; changes in 3.3, 3.4, 9, 10.1, 10.6, 14.4.1, Test Cases, Rationale, Security Considerations).

Position. The framework does not try to give underlying claims a global identity, since the identity of an off-chain entitlement is not something a registry standard can settle, but it no longer leaves reconciliation to the evaluator’s discretion either. Overlap becomes a declared, checkable and acknowledgeable fact, and an unresolved overlap blocks point-valued aggregation:

  • Underlying (3.3). An element may declare underlying: what the position is a claim against (a Unit reference with an optional port, a CAIP asset or a URI) and the share. Two elements with the same underlying and no declaration about each other are overlap candidates.
  • Declarations (3.4, 3.8). Two new relation types, same-claim and distinct-claim, both with a required basis. A parent that contains both children states the relation between the children’s positions, since element identifiers are global. The obligor confirms through the existing description-endorsement acknowledgment; without it the declaration is claimed, not confirmed, and resolvers say which.
  • Resolver rule (3.8). Claim groups by identical element id or declared same-claim; unresolved overlap candidates reported with the declarations and their basis; no merging on labels, shares or counterparties.
  • Aggregation rule (10.6). Over unresolved candidates a composable method must not return a point monetary value: interval (low: counted once, high: counted separately, no point value) or insufficient-evidence. Every receipt over a composite lists its claim groups and candidates under inputs.overlaps with the treatment applied. Composability stays necessary and is now explicitly not sufficient: the precondition for addition is a resolved claim set, not a flag in the method descriptor.
  • Worked example (Units 8 to 11, exception vector 7). One 40% revenue entitlement on the summarizer skill business held as a token position and as a contractual right: different references and element ids, same underlying. The composite that declares same-claim (basis: the tokenization terms) is valued at 45.12, counted once; the composite that declares nothing gets an interval receipt (45.12 to 90.24) and an insufficient-evidence receipt. node tools/resolve.js --unit 11 prints the unresolved overlap, and the resolver rejects a point-valued receipt over it.

No contract change and no new dependency; the method descriptors are unchanged because the rule sits above the methods.

1 Like

Thanks, this was a gap: the text defined “current” per assertion and let receipts cite observation assertion ids, but said nothing about what a resolver does with a receipt whose cited observation is later revoked. The revision is now in the repository and in PR #2095 (Sections 6.4, 10.3, 10.6, exception vector 8, registry behavior 8, Rationale and Security Considerations). In short:

  1. Statuses do not cascade (Section 6.4). Revoking O does not change V, because only V’s asserter can revoke or supersede V; otherwise any observer could silence any evaluator. The dependency is carried by the citation and the consequence is placed on presentation.
  2. Resolvers must show an evidence status with every receipt (Section 10.6), from the status and validity window of each cited observation: withdrawn (a cited observation is revoked), superseded, unresolved (a citation does not resolve to an observation), expired (a cited observation was outside its window at the receipt’s as-of time), supported, or not-cited; first applicable row wins. For each cited observation the resolver lists its id, status, supersededBy and whether it was inside its window at the as-of time.
  3. A receipt on withdrawn evidence must not be presented as current, while its own status stays active; the evaluator should supersede it (corrected receipt or insufficient-evidence). That is the answer to “should the resolver flag V”: yes, and it must also stop presenting V as current, but it must still return V on request.
  4. The time distinction you asked for is explicit: an observation whose validity window ended after the receipt’s as-of time is not withdrawn evidence; the receipt is a statement as of that time, its evidence status stays supported, and resolvers must not mark it non-current on that ground. Revocation says the observation was wrong; the end of a window says only that the window has passed. (If the window had already ended at the as-of time, the status is expired: visible, but the receipt is still returned as current.)
  5. Citations: snapshot.observations lists the observation assertion ids (Section 10.3, SHOULD when the observations are registered); a registered observation of the same subject version whose contentHash equals snapshot.hash counts as cited too, so the receipts already on Sepolia resolve without being re-issued.
  6. Test case: exception vector 8, O -> V -> revoke(O), with the expected resolver output, plus the contrast case. The registry side is testRevokedObservationLeavesDependentReceiptActive (V stays active, the assertion count is unchanged, the observer’s revoke(V) reverts, the evaluator supersedes V); the resolver side is tools/test/resolve-evidence.test.js, which prints, for V: active (not current) ... evidence=withdrawn / cites observation O revoked: withdrawn by its asserter; the receipt rests on withdrawn evidence and is not presented as current, and for V2: active ... evidence=supported / cites observation O2 active: validity ended after the receipt's as-of time; not a withdrawal, the receipt stands as of its as-of time.

No contract change, no schema change and no new dependency.

2 Likes

What happens if both positions omit underlying or use different URIs for the same revenue entitlement?

Would the resolver still allow a point-valued sum because no overlap candidate was detected? Absence of a detected overlap does not establish that the claims are distinct.

Could you add a test for this case and clarify what evidence is required before treating those positions as additive?

1 Like

Agreed, and your sentence is the rule the text was missing: absence of a detected overlap does not establish that the claims are distinct. A revision that defines the resolved claim set positively, with the test you ask for, is in progress; I will post it here when it is in the repository and in PR #2095.

1 Like

Agreed, and your sentence is the rule the text was missing: absence of a detected overlap does not establish that the claims are distinct. As revised on 23 October, Section 3.8 only raised a candidate when two underlying.ref were equal, and Section 10.6 required a “resolved claim set” without defining one, so two positions that omit underlying, or that name one entitlement under two URIs, raised nothing and could have been summed. The revision is now in the repository and in PR #2095 (Sections 3.3, 3.4, 3.8, 10.1, 10.6, exception vector 9, Rationale and Security Considerations).

  1. The resolved claim set is defined positively (Section 3.8). Within a boundary, every pair of positions (financial-position elements, of the Unit itself or reached through contained Units) is either in one claim group (same element id or declared same-claim), or resolved as distinct by a distinct-claim declaration with a basis, or it is an unresolved pair. A missing underlying, two different underlying references, two URIs that may name one entitlement, or simply “no overlap detected” all land in the last class. The overlap candidate (same underlying) is now just a marked subclass of it. Section 10.6 applies unchanged to unresolved pairs: no point monetary value, only an interval (counted once / counted separately, no point value) or insufficient-evidence, and a resolver reports a point value over an unresolved pair as non-conforming. Nothing infers distinctness from reference syntax: two different canonical identifiers can wrap one revenue stream, which is the case of your first question.
  2. Keeping it linear: distinct-claim (and same-claim) take a set form, "elements": [...], so one declaration per definition covers all of its positions. The worked examples now carry it, including the ones that were silent additions before this revision (the skill business with five positions and the swarm with four across its sub-units).
  3. The evidence you asked about: a distinct-claim declaration with a checkable basis (separate source agreements, different obligors, or disjoint cash-flow schedules). It is reported as claimed until the account that controls the underlying (its issuer or obligor, the relation’s counterparty, or the Definition Controller of the underlying Unit) endorses either position through the existing description-endorsement acknowledgment, and as confirmed after; the reference resolver reads the endorsements and prints which. Alternatively the evaluator resolves the pair on its own evidence: inputs.overlaps gained the treatment distinct, basis required; a point value without one of the two is non-conforming. The framework still does not decide whether two references are the same claim; it decides that addition needs someone to have said, with a basis, that they are not.
  4. The test: exception vector 9 with two new worked Units. Unit 12 lists the token position without underlying and the assignment under a URL, with an underlying that names the fee stream under yet another URI, and declares nothing: no overlap candidate, one unresolved pair, receipts interval 45.12 to 90.24 and insufficient-evidence. Unit 13 holds two tranches of 40% each on the same underlying and declares them distinct with both agreements as basis: monetary 90.24 with treatment distinct, reported as distinct (claimed). tools/test/resolve-claims.test.js runs both through the resolver, checks that a point-valued receipt over Unit 12’s pair is rejected and accepted once the receipt states distinct with a basis, and checks the claimed-to-confirmed path on an endorsement by the controller of the underlying.

One consequence we accepted: the definitions of Units 1 and 4 registered on Sepolia on 21 October predate the declaration, so the resolver now reports their net-equity receipts as point values over unresolved pairs; that is the rule working on definitions written before it existed, and it is recorded as such in the evidence document. No contract change, no method descriptor change, no new dependency.

1 Like

Hey. Does the endorsement have to explicitly cover the distinct-claim relation and its basis?

Suppose an issuer endorses position A before a parent Unit declares A distinct from B. Could that existing endorsement make the new relation appear confirmed even though the issuer never reviewed B?

I’d expect confirmation to bind to the specific relation and definition version. Endorsing one position alone seems insufficient to confirm that two claims do not overlap.

1 Like

Yes, that is the right expectation, and the current text does not bind it tightly enough: it lets an endorsement of either element stand for the declaration. The revision binds confirmation to the relation itself (its own identifier) on the declaring version, so an earlier endorsement of A alone, or one on another version, confirms nothing about “A distinct from B”. Coming shortly with the test.

The revision is in the repository and in PR #2095 (Sections 3.4, 3.8, 4 and 5, exception vector 9, registry behavior 9, Rationale and Security Considerations).

  1. The subject of a confirming endorsement is the declaration itself. An identity relation now has an identifier, relationId = keccak256(JCS(relation)), the canonical hash of the relation object exactly as it appears in the definition, basis included (Section 3.4). A same-claim or distinct-claim declaration is confirmed only by a description-endorsement whose subject is that relationId, recorded on the Unit version that carries the declaration, by an account that controls the underlying of a covered position (issuer or obligor, the relation’s counterparty, or the Definition Controller of the underlying Unit). Confirmation is assessed per pair: one such account for both positions, or one for each (Section 3.8).
  2. Your scenario is now explicitly excluded: an endorsement of an element confirms the description of that element and nothing about its relation to any other element, and an endorsement recorded on another version, or before the declaration existed, confirms nothing about the declaration. The issuer who endorsed A in some earlier version never saw “A distinct from B”, and the text no longer lets that endorsement stand for it (Sections 3.8 and 5.1).
  3. No contract change: the registry’s acknowledge(unitId, version, elementId, kind) always treated the subject as an opaque identifier, so it records an endorsement of a relationId exactly like one of an elementId, per version and per account (registry behavior 9, with a test). The Sepolia deployment can therefore carry a confirmed declaration with a single acknowledge.
  4. Tests: a relation identifier vector in the Test Cases (JCS string and hash); examples.json lists every declaration of the worked examples with its relationId; tools/test/resolve-claims.test.js covers an endorsement of the relation by the controller of the underlying (confirmed), an endorsement of position A alone, both before the declaration (on Unit 8) and on the declaring version itself (claimed), an endorsement of the relation on another version or by an account that controls no covered position (claimed), and the different-obligor case, where a pair is confirmed only when an account for each side has endorsed the declaration.

How would the interval rule in Section 10.6 work for liabilities? Suppose two entries each describe a USD 100 debt, and it’s unclear whether they’re the same obligation. Counting it once contributes -100 to net equity; counting both contributes -200. Under the stated rule, low would be -100 and high would be -200, so the bounds end up reversed.

Could the method determine the lower and upper bounds across the possible claim resolutions instead and return insufficient-evidence if it can’t justify them? Adding a liability case alongside the revenue example would help clarify this.

1 Like

You are right, and the example makes it plain: Section 10.6 wrote the interval as counted once for low and counted separately for high, which silently assumes every position contributes with a positive sign. The revision defines the bounds as the minimum and maximum of the method’s result over the admissible resolutions of the unresolved pairs, with low <= high as a hard invariant, and adds a liability case and a mixed case next to the revenue example. Posting it here once it is in the repository and PR #2095.

2 Likes

The revision is in the repository and in PR #2095 (Sections 3.8, 10.5, 10.6, 14.4.1, exception vector 10, Rationale).

  1. Definition. The bounds of the interval that an unresolved pair forces are extremes over resolutions, not a count. A resolution reads every unresolved pair of the boundary as one claim or as two; it is admissible when the claim groups it induces are consistent with every declared same-claim and distinct-claim relation and with transitivity. Under a resolution the method counts each claim once (where the references of one claim carry different quantities, the method states which it uses, or takes the smallest for low and the largest for high). low is the minimum and high the maximum of the method’s result over all admissible resolutions (Section 10.6). Sign takes care of itself: for two held positions the once reading is the minimum, for two owed positions it is the maximum, and a mixed boundary has no single reading at either end.
  2. Decomposition and its limit. When no two unresolved pairs share a position, each pair contributes its own minimum and maximum and the bounds are the sums. When they do share positions, the method either enumerates the admissible resolutions and takes the extremes or returns insufficient-evidence; an interval not computed over admissible resolutions is non-conforming.
  3. Invariant. low MUST NOT exceed high (Section 10.5); validators and resolvers reject a receipt in which it does, which is exactly your example. The reference resolver goes one step further: it recomputes every interval’s bounds from the receipt’s own per-position inputs by enumerating the admissible set partitions and reports an interval that differs from them.
  4. A side rule that the liability case needed (Section 3.8): a position is owed when the Unit includes it under owes or commits and held otherwise; a claim has one direction, so a held and an owed position are never one claim. They are distinct by construction and never an unresolved pair, which is what keeps the readings of a mixed boundary meaningful (no resolution reads a revenue right and a debt as one claim) and the declaration burden where it belongs.
  5. The liability case, next to the revenue one. Unit 14 owes two positions that each describe a debt of 100 to the same provider, an invoice and a ledger entry, with no declaration: net equity is the interval [-200.00, -100.00] (or insufficient-evidence), the once reading being the upper bound. Unit 15 holds the revenue pair of Unit 12 and owes the debt pair: two unresolved pairs, one on each side, four cross-side pairs distinct by construction, bounds [-154.88, -9.76] = [45.12 - 200.00, 90.24 - 100.00]. Exception vector 10 states both, and tools/test/resolve-intervals.test.js checks them, rejects the reversed receipt, reports an interval taken from the wrong corner ([-154.88, -54.88], the all-once reading) with the recomputed bounds, and enumerates a chained case (A?B, B?C, A distinct from C: [-300, -200] over three admissible resolutions).

No contract change and no schema change. Thank you for the example; it made the sign assumption visible in one line.

1 Like

Binding the endorsement to the relation hash and the declaring version addresses my concern. The tests excluding element-only endorsements cover the scenario I had in mind. Thanks for following through.

2 Likes