logo

Representable Contract State

an XML-based canonical view of EVM contract state


XML Namespace and Bindings

This page describes the XML namespace and binding attributes used by contracts that implement IXMLRepresentableState (and the optional IXMLRepresentableStatePart interface).

It is the detailed companion to the concept/spec page.

Namespace

This standard defines a dedicated XML namespace for EVM-state bindings:

  • Namespace URI: urn:evm:state:1.0
  • Recommended prefix: evmstate

A typical root element in a template looks like:

<Contract xmlns="urn:example:instrument"
xmlns:evmstate="urn:evm:state:1.0">
...
</Contract>

All attributes in this namespace (evmstate:*) are processing instructions for an off-chain renderer. The final rendered XML representation may keep or strip these attributes, depending on the use-case.

An element that carries one or more evmstate:* attributes is called a binding element.


Function bindings

Function bindings connect XML elements to contract view functions. A binding describes how to call a function and how to decode the result.

There are two equivalent styles:

  • Signature form (preferred) - human-readable and self-describing.
  • Selector form (low-level) - uses ABI selectors and explicit return types.

Signature form (preferred)

<Notional
evmstate:call="notional()(uint256)"
evmstate:format="decimal" />
  • evmstate:call contains a Solidity function signature string of the form
    functionName(inputTypes...)(outputTypes...), without spaces.
  • The renderer MUST:
    • compute the function selector as keccak256("notional()")[0:4];
    • use the declared output type to decode the return data (see below for supported output types).

The signature form is usually easier to read and maintain.

Selector form (low-level)

<Notional
evmstate:selector="0x70a08231"
evmstate:returns="uint256"
evmstate:format="decimal" />
  • evmstate:selector is a 4‑byte hex selector as a string with 0x prefix.
  • evmstate:returns is an ABI type string describing the return type (see below for supported return types).

The renderer MUST call the contract using the given selector and decode using the specified type.

Preference and errors

  • If both evmstate:call and evmstate:selector are present for a single binding, the renderer MUST prefer evmstate:call and MAY treat the presence of selector as an error.

Target location (single binding)

A single binding can write either to the element’s text content or to one of its attributes.

Text content (default)

If no explicit target is given, the renderer writes the value into the element’s text content:

<Notional
evmstate:call="notional()(uint256)"
evmstate:format="decimal"
evmstate:scale="2" />

becomes, for example:

<Notional>1000000.00</Notional>

Attribute target

If evmstate:target is present and non‑empty, its value is interpreted as the local name of an attribute to populate on the same element:

<Party
evmstate:call="partyALEI()(string)"
evmstate:target="id" />

might render as:

<Party id="TESTPNM19D40DI1WT772" />

(if the string TESTPNM19D40DI1WT772 is the return value of the view partyALEI()).

Rules:

  • The renderer MUST create or overwrite an attribute with the given name.
  • The renderer MUST NOT modify the element’s text content because of this binding.
  • Bindings are always attached to elements, not attributes (XML attributes cannot carry attributes of their own).

Multiple bindings per element

Sometimes a single XML element should aggregate several values, for example an amount as text plus a currency attribute. For this, the binding schema supports both:

  • Single‑binding mode, using the singular attributes, and
  • Multi‑binding mode, using plural attributes with semicolon‑separated lists.

Single‑binding mode

Only the singular attributes are present:

  • evmstate:call
  • evmstate:selector
  • evmstate:returns
  • evmstate:format
  • evmstate:scale
  • evmstate:target

The element describes exactly one binding.

Multi‑binding mode (semicolon‑separated lists)

When any of the plural attributes are present, the element is in multi‑binding mode:

  • evmstate:calls
  • evmstate:selectors
  • evmstate:returnsList
  • evmstate:formats
  • evmstate:scales
  • evmstate:targets

Each of these attributes is interpreted as a semicolon‑separated list. The lists are interpreted positionally.

Example:

<Amount
evmstate:calls="notional()(uint256);currency()(string)"
evmstate:formats="decimal;string"
evmstate:scales="2; "
evmstate:targets="; currency" />

Semantics:

  • Split each plural attribute at ';'.
  • Trim whitespace around each entry.
  • Let N be the length of the calls list.
  • For index i = 0..N-1:
    • calls[i] is the i‑th function signature (optional).
    • selectors[i] is the i‑th selector (optional).
    • returnsList[i] is the i‑th return type (optional).
    • formats[i] is the i‑th format specifier (optional).
    • scales[i] is the i‑th decimal scale (optional).
    • targets[i] is the i‑th target specifier (optional).

Missing list entries are treated as empty strings. If both calls[i] and selectors[i] are empty, that index is ignored.

For each binding index i:

  • If targets[i] is empty or missing → write to the element’s text content. If several bindings write text, later bindings overwrite earlier ones.
  • If targets[i] is non‑empty → write to the attribute with that name. Text content MUST NOT be changed for that binding.

Example: text + attribute

The example above:

<Amount
evmstate:calls="notional()(uint256);currency()(string)"
evmstate:formats="decimal;string"
evmstate:scales="2; "
evmstate:targets="; currency" />

MUST render to something equivalent to:

<Amount currency="EUR">1000000.00</Amount>

Arrays and multi‑binding mode

For the optional array binding profile, array‑valued output types MUST NOT be used in multi‑binding mode. A renderer that implements the array profile MUST treat a multi‑binding with an array return type as an error.

Arrays are handled exclusively by:

  • a single binding on the array container, plus
  • item‑field bindings inside a template row (see below).

Formatting

The optional attributes evmstate:format / evmstate:scale (or their plural forms in multi‑binding mode) describe how to render decoded ABI values as strings.

If format is absent or empty, a type‑specific default is used. If scale is absent or empty, it defaults to 0.

Integer formatting and scaling

For uint* and int* types, implementations MUST support at least:

  • Default → same as "decimal".
  • "decimal" → base‑10 representation, optionally with scaling.
  • "hex" → lower‑case hex with 0x prefix.
  • "iso8601-date" → interpret the integer as a UNIX timestamp in seconds since epoch and render a UTC calendar date YYYY-MM-DD.
  • "iso8601-datetime" → interpret the integer as a UNIX timestamp in seconds and render a UTC timestamp such as 2025-01-02T00:00:00Z.

Scaling is controlled via evmstate:scale:

<Amount
evmstate:call="notional()(uint256)"
evmstate:format="decimal"
evmstate:scale="2" />

Here the raw integer is scaled by 10^(-2). For example, 12345 becomes "123.45".

Address

For address:

  • Default format is "address".
  • "address" → hex with 0x prefix and an [ERC‑55] checksum.

Boolean

For bool:

  • Default format is "boolean".
  • "boolean""true" or "false".

Bytes and fixed‑size bytes

For bytes and bytesN:

  • Default is "hex".
  • "hex" → hex with 0x prefix.
  • "base64" → Base64 representation.

String

For string:

  • Default is "string".
  • "string" → UTF‑8 text as returned.

Unknown formats

Implementations MAY support additional formats. If the renderer encounters an unknown evmstate:format, it SHOULD treat this as an error.

In multi‑binding mode, evmstate:formats and evmstate:scales provide per‑binding values:

  • formats[i] applies to the i‑th binding.
  • scales[i] applies to the i‑th binding; empty entries mean scale 0.

Array binding profile (optional)

The array binding profile is an optional extension that lets array‑valued bindings render as a sequence of repeated child elements. It is designed to play nicely with XSD and XSLT.

Renderers MAY implement this profile. If they do, they MUST follow the rules in this section. If they do not implement it, they MUST treat any use of the array‑specific attributes (evmstate:item-element, evmstate:item-field) as an error.

Conceptual recursive view of arrays and structures

It is often useful to think about arrays and structs/tuples as if the contract had additional “virtual” element accessors. This is a conceptual model only - these accessors do not have to exist on-chain. The renderer internally performs an equivalent rewrite to scalar bindings.

Arrays

Assume a view function returning an array of some type A:

function arrayView() external view returns (A[] memory);

and a template element

<ArrayElement
evmstate:call="arrayView()(A[])"
evmstate:item-element=""
evmstate:format="..." />

The presence of an empty evmstate:item-element means:

  • conceptually, there exists a virtual accessor
    arrayElement(uint256 index) returns (A)
    that is never implemented on-chain; and
  • the template above is equivalent to:
<ArrayElement evmstate:call="arrayElement(0)(A)" evmstate:format="..." />
<ArrayElement evmstate:call="arrayElement(1)(A)" evmstate:format="..." />
<ArrayElement evmstate:call="arrayElement(2)(A)" evmstate:format="..." />
...

In the renderer, this is realised by:

  1. Calling arrayView() once to obtain the full A[].
  2. Repeating the element once per index and feeding A[i] into each clone.

No arrayElement(uint256) function is ever called on the contract.

If a non-empty item element is present, e.g.

<Array evmstate:call="arrayView()(A[])" evmstate:item-element="Item">
    <Item evmstate:format="...">
        ...
    </Item>
</Array>

this is conceptually equivalent to

<Array>
    <Item evmstate:call="arrayView()(A[])" evmstate:item-element="" evmstate:format="...">
        ...
    </Item>
</Array>

which then expands as above into multiple Item rows. The formal semantics for this are given in the following subsections.

Structures / tuples

For a function that returns a tuple (or struct) such as

function method() external view returns (T1, T2, T3);

one may conceptually imagine separate component accessors

methodT1() external view returns (T1);
methodT2() external view returns (T2);
methodT3() external view returns (T3);

and a template

<Structure evmstate:call="method()(T1,T2,T3)" evmstate:targets=";attr2;attr3" />

as equivalent to

<Structure
evmstate:calls="methodT1()(T1);methodT2()(T2);methodT3()(T3)"
evmstate:targets=";attr2;attr3" />

In practice, the renderer calls method() once, decodes the tuple (T1,T2,T3) into its components, and then distributes them according to targets. Direct tuple bindings in non-array contexts are a possible future extension; the current reference implementation only uses tuples as array elements in the array binding profile.

Multiple arrays and “zipped” rows (future extension)

The same mental model extends naturally to several array-valued bindings that are “zipped” into rows. For example:

<Array>
    <Item
        evmstate:calls="arrayView1()(A[]);arrayView2()(B[])"
        evmstate:item-element=""
        evmstate:targets=";attr"
        evmstate:formats="..." />
</Array>

can be conceptually understood as if there were virtual element accessors

arrayElement1(uint256 i) returns (A);
arrayElement2(uint256 i) returns (B);

and the template was equivalent to:

<Array>
    <Item evmstate:calls="arrayElement1(0)(A);arrayElement2(0)(B)" evmstate:targets=";attr" evmstate:formats="..." />
    <Item evmstate:calls="arrayElement1(1)(A);arrayElement2(1)(B)" evmstate:targets=";attr" evmstate:formats="..." />
    ...
</Array>

An implementation would call both arrays once, zip them in memory, and apply the multi-binding per row. This pattern is not part of the 1.0 array binding profile and is not implemented by the reference renderer yet. It is documented here as a natural future extension.

Supported array types

The profile supports bindings whose declared output type is one of:

  • T[] or T[M], where T is any scalar ABI type supported by the core profile.
  • tuple(T0,...,Tn-1)[] or tuple(T0,...,Tn-1)[M], where each Ti is a scalar ABI type (e.g. int256, uint256, address, bool, string, bytes, bytesN).

Nested arrays such as uint256[][] or tuple(uint256[],uint256)[] are out of scope and MUST be treated as an error in the current 1.0 profile.

Array containers and evmstate:item-element

An XML element E is an array container if:

  1. It is in single‑binding mode and has a binding via evmstate:call or evmstate:selector / evmstate:returns.
  2. The declared output type of that binding is an array type supported by this profile.
  3. It has an attribute evmstate:item-element="N", where N is a non‑empty XML local name.

Within E, the renderer MUST locate the template row as follows:

  • Among the direct children of E, find the first element whose local name is exactly N.
  • If such an element exists, it is the template row T*.
  • If not, the renderer SHOULD treat this as an error.

Before inserting any rendered rows, the renderer MUST remove the template row T* from the document. If rendering produces zero rows, E will simply have no such child elements.

evmstate:item-element is only meaningful on array containers and SHOULD NOT be used elsewhere.

Evaluation semantics

Given chain‑id C, contract address A, block‑number B, and an array container E:

  1. Evaluate the array‑valued binding of E at block B (using the standard function binding rules), producing a sequence items[0..N-1].

    • If the element type is scalar T, then items[i] is a scalar.
    • If the element type is tuple(T0,...,Tn-1), then items[i] = (v0,...,v{n-1}).
  2. For each index i = 0..N-1:

    • Deep‑clone the template row T* (including descendants and attributes) to produce R.
    • Within R and its descendants, process any evmstate:item-field attributes using items[i] as the current row value.
    • Insert R as a child of E, after any previously inserted rows.
  3. If N = 0, T* is removed and no rows are inserted.

Array‑valued bindings MUST NOT appear in multi‑binding mode in this profile.

evmstate:item-field inside template rows

Inside the subtree rooted at the template row T*, elements MAY carry:

evmstate:item-field="k"

where k is a non‑negative integer index into the array element (not into the array itself).

Let the array element type be:

  • scalar T (e.g. uint256[]), or
  • tuple tuple(T0,...,Tn-1) (e.g. tuple(int256,uint256)[]).

For a given row index i and an element X with evmstate:item-field="k":

  1. Let value = items[i].
  2. Determine the selected component v:
    • For scalar T: treat value as a tuple (v0) of length 1. The only valid index is k = 0; other values MUST be treated as an error.
    • For tuple types: value = (v0,...,v{n-1}). Valid indices are 0 <= k < n, and v = vk. Other values MUST be treated as an error.
  3. Render v to a string using X’s evmstate:format / evmstate:scale (or type defaults).
  4. Place the rendered value:
    • If X has evmstate:target="attrName", set or overwrite an attribute attrName on X with that value; do not change text.
    • Otherwise, replace the text content of X.

Restrictions:

  • evmstate:item-field is only meaningful inside the subtree of a template row in an array container.
  • Within an element that carries evmstate:item-field, only single‑binding mode is allowed. It MUST NOT be combined with the multi‑binding attributes.

Example: scalar array

Assume a contract function

function couponAmounts() external view returns (int256[] memory);

A template that renders each coupon in its own element:

<Coupons
xmlns:evmstate="urn:evm:state:1.0"
evmstate:call="couponAmounts()(int256[])"
evmstate:item-element="Coupon">

  <!-- Template row, cloned once per array element -->
<Coupon
evmstate:item-field="0"
evmstate:format="decimal"
evmstate:scale="2" />
</Coupons>

If the function returns three entries, the rendered XML might be:

<Coupons>
  <Coupon>1000000.00</Coupon>
  <Coupon>1000000.00</Coupon>
  <Coupon>1000000.00</Coupon>
</Coupons>

Example: array of tuples (payment schedule)

Assume

struct Cashflow {
    int256 amount;      // 18-decimal
    uint256 payDate;    // unix timestamp
}

function cashflows() external view returns (Cashflow[] memory);

ABI return type: tuple(int256,uint256)[].

A template that renders a payment schedule:

<PaymentSchedule
xmlns:evmstate="urn:evm:state:1.0"
evmstate:call="cashflows()(tuple(int256,uint256)[])"
evmstate:item-element="Payment">

  <!-- Template row, cloned once per cashflow -->
  <Payment>
    <PaymentDate
        evmstate:item-field="1"
        evmstate:format="iso8601-date" />
    <Amount
        evmstate:item-field="0"
        evmstate:format="decimal"
        evmstate:scale="2" />
  </Payment>

</PaymentSchedule>

which might render as:

<PaymentSchedule>
  <Payment>
    <PaymentDate>2026-01-02</PaymentDate>
    <Amount>1000000.00</Amount>
  </Payment>
  <Payment>
    <PaymentDate>2026-04-02</PaymentDate>
    <Amount>1000000.00</Amount>
  </Payment>
  <!-- ... -->
</PaymentSchedule>

### Example: array of tuples (settlement history)

Assume

```solidity
struct Settlement {
    uint256 time;   // unix timestamp
    int256  value;  // scale 2
}

function settlementHistory() external view returns (Settlement[] memory);

ABI return type: tuple(uint256,int256)[].

A template that renders the settlement history:

<History
xmlns:evmstate="urn:evm:state:1.0"
evmstate:call="settlementHistory()(tuple(uint256,int256)[])"
evmstate:item-element="Settlement">

  <!-- Template row, cloned once per settlement -->
  <Settlement>
    <time
        evmstate:item-field="0"
        evmstate:format="iso8601-datetime" />
    <value
        evmstate:item-field="1"
        evmstate:format="decimal"
        evmstate:scale="2" />
  </Settlement>

</History>

More complex document shapes (grouped summaries, tables, inline lists, …) can be derived from this repeated‑element representation using standard XML tools such as XSLT.


Context bindings

Required context bindings: chain, contract, block

The XML representation MUST identify the chain, contract, and block it represents. To that end, this ERC reserves the following attributes in the evmstate namespace on the root element:

  • evmstate:chain-id
  • evmstate:contract-address
  • evmstate:block-number

Typical template:

<Contract xmlns="urn:example:instrument"
xmlns:evmstate="urn:evm:state:1.0"
evmstate:chain-id=""
evmstate:contract-address=""
evmstate:block-number="">
...
</Contract>

These attributes are context bindings filled by the renderer, not by explicit function calls:

  • evmstate:chain-id → chain ID (e.g. EIP‑155), base‑10 string.
  • evmstate:contract-address → contract address, checksummed hex.
  • evmstate:block-number → block number at which the binding evaluation took place, base‑10 string.

Renderers SHOULD use the evmstate:* namespace for these attributes to avoid collisions with business schemas. They MAY additionally provide non‑namespaced duplicates if downstream tools require them.

Renderer metadata (optional)

In addition to the required context bindings (evmstate:chain-id, evmstate:contract-address, evmstate:block-number), renderers MAY add the following informational attributes to the root element:

  • evmstate:renderer-id – a stable identifier of the renderer implementation (for example, a library or class name).
  • evmstate:renderer-version – a version string of the renderer implementation (for example, the Maven artifact version or semantic version).
  • evmstate:renderer-url – an optional URL pointing to documentation or a project page for the renderer.

These attributes are purely informational and MUST NOT affect the semantics of the representation. Two XML documents that differ only in these attributes MUST be considered equivalent for the purposes of XML-completeness, state comparison, and integrity checks.

Example root element in a template using renderer metadata:

<Contract xmlns="urn:example:instrument"
          xmlns:evmstate="urn:evm:state:1.0"
          evmstate:chain-id=""
          evmstate:contract-address=""
          evmstate:block-number=""
          evmstate:renderer-id=""
          evmstate:renderer-version=""
          evmstate:renderer-url="">
    ...
</Contract>

XML representation and XML-complete contracts

For a given chain-id C, contract address A, and block-number B, and for a contract that implements IXMLRepresentableState, the XML representation at (C, A, B) is defined as follows:

  1. Choose a JSON-RPC provider for chain C.
  2. Call eth_getBlockByNumber (or equivalent) to obtain block B and its number, or use an externally provided B.
  3. Perform all eth_call invocations (for stateXmlTemplate() and for all bound functions) with blockTag = B.
  4. Start from the XML template returned by stateXmlTemplate().
  5. Resolve all bindings as specified above and insert the resolved values.
  6. Fill evmstate:chain-id, evmstate:contract-address, and evmstate:block-number on the root element.
  7. Optionally remove all evmstate:* attributes from the document.

A contract is XML-complete if, for every block B at which its code matches this interface, the following holds:

Given the XML representation at (C, A, B), one can reconstruct all semantically relevant mutable state that influences the contract's future behaviour (up to isomorphism).

This is a semantic property that cannot be enforced by the EVM itself, but it can be audited and tested. Authors of contracts that claim to implement IXMLRepresentableState SHOULD ensure that:

  • Every mutable storage variable that influences behaviour is either:
    • directly bound via an evmstate:call / evmstate:selector, or
    • deterministically derivable from bound values via a public algorithm.
  • Adding new mutable state is accompanied by adding corresponding bindings to the template.

In practice, contracts MAY also expose a separate “state descriptor” view function that lists all bound fields, but this is out of scope for the minimal interface.