logo

Representable Contract State

an XML-based canonical view of EVM contract state


XML Namespace and Bindings

Namespace

This standard defines the XML namespace URI:

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

The XML template MUST declare this namespace, for example:

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

Bindings

Bindings are expressed as attributes in the evmstate namespace on XML elements.

A binding element is any XML element that has one or more attributes in the evmstate namespace.

Function binding

To bind an element or attribute to a contract view function, the template uses either:

  1. Signature form (preferred)
<Notional
    evmstate:call="notional()(uint256)"
    evmstate:format="decimal" />
  • evmstate:call is a Solidity function signature string of the form
    functionName(inputTypes...)(outputTypes...), with no spaces.
  • The renderer MUST:
    • compute the function selector as keccak256("notional()")[0:4], and
    • use the declared output type (uint256) to decode the return data.
  1. 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 a 0x prefix.
  • evmstate:returns is an ABI type string describing the return type.
  • The renderer MUST call the contract using the provided selector and decode using the given type.

If both evmstate:call and evmstate:selector are present, the renderer MUST prefer evmstate:call and MAY treat evmstate:selector as an error.

Core scalar profile vs. arrays

For the core scalar profile, the output type of a binding MUST be a single, non-array ABI type, e.g.

  • uint256, int256
  • address, bool, string
  • bytesN, bytes

Implementations MAY additionally support the optional array binding profile (described below), which allows array and array-of-tuple return types to drive repeated XML elements.

An implementation that does not support the array binding profile SHOULD treat any binding whose declared output type is an array (e.g. uint256[], tuple(uint256,uint256)[]) as an error.


Target location (single binding)

A single binding can either target the element's text content or one of its attributes:

  • If evmstate:target is absent or empty, the renderer MUST replace the element's text content with the rendered value.

Example

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

might render to:

<Notional>1000000.00</Notional>
  • If evmstate:target is present and non-empty, its value is the local name of an attribute to be populated.

Example

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

might render to:

<Party id="LEI-of-Party-A" />

The renderer MUST create or overwrite the attribute with that name on the element. It MUST NOT change the element's text content in this case.

Bindings MUST NOT be attached directly to attributes (XML does not allow attributes on attributes); all evmstate:* attributes are always attached to elements.


Multiple bindings per element

A single XML element can have one or more bindings associated with it.

To make this explicit, we distinguish:

  • Single-binding attributes (no semicolons, exactly one binding):

    • evmstate:call
    • evmstate:selector
    • evmstate:returns
    • evmstate:format
    • evmstate:scale
    • evmstate:target
  • Multi-binding attributes (semicolon-separated lists, interpreted positionally):

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

When any of the plural attributes (evmstate:calls, evmstate:selectors, …) are present, the element is in multi-binding mode:

  • Each list is split on ';', and each part is trimmed of leading and trailing whitespace.
  • The lists are interpreted positionally. For index i:
    • calls[i] is the i‑th function signature (optional).
    • selectors[i] is the i‑th selector (optional).
    • returnsList[i] is the i‑th explicit 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).

Bindings are resolved in order i = 0..N-1, where N is the length of the evmstate:calls list. If both calls[i] and selectors[i] are empty for a given index, that index MUST be ignored. If a list is shorter than N, missing entries MUST be treated as empty strings.

For each binding index i, targets[i] determines whether the value is written to the element's text content or to an attribute:

  • If targets[i] is empty or missing (after trimming), the renderer MUST replace the element's text content with the rendered value for that binding. If multiple bindings for the same element write text, they MUST be applied in index order; later writes overwrite earlier ones.

  • If targets[i] is a non-empty string, the renderer MUST set (create or overwrite) an attribute on the element with that local name and the rendered value as its value. It MUST NOT change the element's text content because of this binding.

When only the singular attributes are present (no evmstate:calls/evmstate:formats/…), the element is in single-binding mode, and the renderer MUST treat evmstate:call / evmstate:selector / evmstate:returns / evmstate:format / evmstate:scale / evmstate:target as describing exactly one binding.

For the optional array binding profile (see below), array-valued return types MUST NOT be used in multi-binding mode. Array handling is restricted to single-binding mode on the array container and to single-binding nodes inside the template row.

Example: single binding to the element's text

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

might render to:

<Notional>1000000.00</Notional>

Example: two bindings – notional as element text, currency as attribute

<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>

Formatting

The optional attribute evmstate:format describes how to convert the decoded ABI value into a text string.
If evmstate:format is absent or empty, a type-specific default is used.

When evmstate:formats is used, each entry formats[i] applies to the i‑th binding in multi-binding mode as described above. Similarly, when evmstate:scale or evmstate:scales are present, scale / scales[i] apply to the corresponding binding; a missing or empty entry is treated as scale 0.

Implementations SHOULD support at least the following combinations:

  • For unsigned integers (uint*) and signed integers (int*):

    • Default → same as "decimal".
    • "decimal" → base‑10 representation, optionally with scaling as described below.
    • "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 in ISO 8601 form YYYY-MM-DD.
    • "iso8601-datetime" → interpret the integer as a UNIX timestamp in seconds since epoch and render a UTC timestamp in ISO 8601 form (e.g. 2025-01-02T00:00:00Z).
  • For address:

    • Default → same as "address".
    • "address" → hex with 0x prefix and ERC‑55 checksum.
  • For bool:

    • Default → same as "boolean".
    • "boolean""true" or "false".
  • For bytes and bytesN:

    • Default → same as "hex".
    • "hex" → hex with 0x prefix.
    • "base64" → base64 representation.
  • For string:

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

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

Scaling

Optionally, an evmstate:scale / evmstate:scales attribute MAY be used for decimal-like integers:

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

This means that the raw integer is scaled by 10^(-scale) before rendering, e.g. 12345 with scale="2" becomes "123.45".


Array binding profile (optional, “Mode B”)

The array binding profile is an optional extension that some renderers may implement.
It allows a single array-valued binding to drive a repeated list of child elements.

Other array-shaped representations (e.g. inline lists, aggregates) are intentionally left to off‑chain post-processing (for example via XSLT) and are not part of this profile.

Supported array output types

The array binding 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.

Nested arrays (e.g. uint256[][], tuple(uint256[],uint256)[]) are out of scope.
A renderer that implements this profile SHOULD treat such types as an error.

Array containers and template rows

An XML element E is an array container if all of the following hold:

  • It is in single-binding mode and has a binding via evmstate:call or evmstate:selector / evmstate:returns.
  • The declared output type of that binding is an array type supported by this profile.
  • The element has an attribute evmstate:item-element whose value is a non-empty XML local name, denoted N.

Within an array container E, the renderer locates the template row as follows:

  • It searches among the direct children of E for the first element whose local name is exactly N.
  • If such a child exists, that element is the template row T*.
  • If no such child exists, 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 have no child corresponding to the template.

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

Evaluation semantics (informal)

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 normal function-binding rules, yielding a sequence items[0..N-1].

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

    • Deep-clone the template row T* (including its descendants and attributes) to a new element R.
    • Within R and its descendants, process any evmstate:item-field attributes as described below, using items[i] as the current row value.
    • Insert R as a child of E, after any previously inserted rows, preserving the original document order of other children of E.
  3. If N = 0, the renderer MUST remove T* and MUST NOT insert any rows derived from it.

Array-valued bindings MUST NOT be used in multi-binding mode (evmstate:calls, evmstate:selectors, etc.) in this profile.

Item-field bindings

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

evmstate:item-field="k"

where k is a non-negative integer index into the array element.

Let the ABI 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 a node X inside the cloned row R that has evmstate:item-field="k":

  1. Determine value = items[i].

  2. Determine the selected component v:

    • If the element type is scalar T, value is a single scalar. For this profile, it is treated as a tuple (v0) of length 1. The only valid index is k = 0. If k != 0, this SHOULD be treated as an error.

    • If the element type is a tuple tuple(T0,...,Tn-1), then value = (v0,...,v{n-1}). The index k MUST satisfy 0 <= k < n, and v = vk. Otherwise this SHOULD be treated as an error.

  3. Render v to a string using the existing scalar formatting rules on X (evmstate:format, evmstate:scale). If evmstate:format is absent on X, the default for the ABI type of v is used.

  4. Place the rendered string:

    • If X has an attribute evmstate:target="attrName", the renderer sets (creates or overwrites) an attribute attrName on X with the rendered string as its value and MUST NOT change X’s text content because of this binding.

    • If X has no evmstate:target attribute, the renderer MUST replace the text content of X with the rendered string.

The evmstate:item-field attribute is only meaningful inside the subtree of a template row in an array container; its use elsewhere SHOULD be treated as an error.

Within a node that carries evmstate:item-field, only single-binding mode is allowed. It MUST NOT be combined with the multi-binding attributes (evmstate:calls, evmstate:selectors, etc.) in this profile.

Example: scalar array

Contract function:

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

Template:

<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 amounts, 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)

Contract function:

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

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

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

<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>

Rendered XML:

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

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


Chain and contract identification

The XML representation MUST identify the chain, contract, and block that it represents.

This standard reserves the following attributes in the evmstate namespace on the root element:

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

Example root element in the 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:

  • The renderer MUST set evmstate:chain-id to the EIP‑155 chain ID, as a base‑10 string.
  • The renderer MUST set evmstate:contract-address to the contract address, as a checksummed hex address.
  • The renderer MUST set evmstate:block-number to the block number at which the representation was evaluated, as a base‑10 string.

These fields are filled based on the RPC context (chain id, contract address, and block tag) and do not correspond to actual contract calls.

After rendering, the root element in the final XML might look like:

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

The renderer SHOULD set these attributes in the evmstate namespace (e.g. evmstate:chain-id, evmstate:contract-address, evmstate:block-number) to avoid collisions with existing attributes defined by the business XML schema. Implementations MAY additionally provide non-namespaced duplicates if required by downstream tooling.


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.