Representable Contract State
an XML-based canonical view of EVM contract state
Usage and Examples
This page shows how to use the Representable Contract State interfaces and XML bindings in practice:
- As a contract author (how to expose state as XML, including partial views / parts).
- As an off-chain renderer (how to build the canonical XML representation at a block).
- As an oracle or integration (how to consume events that reference representable state by reference rather than by value).
For the formal specification, see the concept/spec page and the detailed XML Namespace and Bindings description.
For contract authors
Complete State - IXMLRepresentableState
A contract that wants to expose its complete state implements:
interface IXMLRepresentableState {
function stateXmlTemplate() external view returns (string memory);
}
The function SHOULD return a constant XML template string (independent of mutable state). All dynamic data is pulled in via evmstate:* bindings inside that template.
Example
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.23;
import "./IRepresentableState.sol";
contract MinimalInstrument is IXMLRepresentableState {
address public owner;
uint256 public notional;
string public currency;
uint256 public maturityDate;
bool public active;
constructor(address _owner, uint256 _notional, uint256 _maturityDate) {
owner = _owner;
notional = _notional;
currency = "EUR";
maturityDate = _maturityDate;
active = true;
}
function stateXmlTemplate() external pure override returns (string memory) {
return
"<Instrument xmlns='urn:example:instrument'"
" xmlns:evmstate='urn:evm:state:1.0'"
" evmstate:chain-id=''"
" evmstate:contract-address=''"
" evmstate:block-number=''>"
"<Owner evmstate:call='owner()(address)' evmstate:format='address'/>"
"<Notional"
" evmstate:calls='notional()(uint256);currency()(string)'"
" evmstate:formats='decimal;string'"
" evmstate:scales='2;'" /* 2 decimals for notional */
" evmstate:targets=';currency'/>"
"<MaturityDate evmstate:call='maturityDate()(uint256)'"
" evmstate:format='iso8601-date'/>"
"<Active evmstate:call='active()(bool)' evmstate:format='boolean'/>"
"</Instrument>";
}
}
Off-chain, a renderer will:
- Fetch this template at a specific block
B. - Evaluate all
evmstate:*bindings at the same blockBand apply the specified transformations. - Fill
evmstate:chain-id,evmstate:contract-address,evmstate:block-number. - Optionally strip
evmstate:*attributes.
The result is a canonical XML representation of the contract state at (chain-id, address, block).
Optional: versions and hashes
To make caching and integrity checks easier, contracts MAY implement:
interface IRepresentableStateVersioned {
function stateVersion() external view returns (uint256);
}
interface IRepresentableStateHashed {
function stateHash() external view returns (bytes32);
}
interface IXMLRepresentableStateVersionedHashed is IXMLRepresentableState, IRepresentableStateVersioned, IRepresentableStateHashed {}
Typical pattern:
uint256 private _stateVersion;
function update(... ) external {
// mutate storage
_stateVersion += 1;
}
function stateVersion() external view returns (uint256) {
return _stateVersion;
}
function stateHash() external view returns (bytes32) {
return keccak256(abi.encode(owner, notional, currency, maturityDate, active));
}
Off-chain tools can then use (chainId, contract, blockNumber, stateHash) as a compact key for signatures, audit trails, or cache entries.
Partial views - IXMLRepresentableStatePart
Some applications only need a projection of the full state, e.g. a settlement context or a risk summary. For this, the ERC defines the optional interface:
interface IXMLRepresentableStatePart {
function statePartXmlTemplate(uint256 partId) external view returns (string memory);
}
Contracts that provide both the canonical full representation and partial views can define:
interface IXMLRepresentableStateWithParts is IXMLRepresentableState, IXMLRepresentableStatePart {}
Renderer Implementation (off-chain)
Although concrete implementations may differ, any conforming renderer MUST:
- Choose a block number
B(e.g. by readingeth_getBlockByNumber("latest")and taking its number). - Call
stateXmlTemplate()(or astatePartXmlTemplate) withblockTag = B. - Parse the XML and discover all binding elements (those with
evmstate:*attributes). - For each binding, execute the corresponding
eth_callwithblockTag = Band decode the result according to theevmstate:*attributes (including the optional array profile). - Fill the context bindings on the root element:
evmstate:chain-idevmstate:contract-addressevmstate:block-number
- Optionally strip all
evmstate:*attributes for downstream consumers.
Renderers MAY additionally:
- Use
stateVersion()for caching (e.g. only re-render when the version increases). - Recompute
stateHash()from the values they used and compare it to the on-chain value.
A Java implementation (EvmXmlRenderer) in this project follows this algorithm. Instead of implementing your own renderer, consider opening a feature request via the issue tracker if you need additional functionality.
Usage of the Renderer - e.g. by an Off-Chain Oracle
- The off-chain oracle fixes the contract address
Aon chainCand the desired blockB. This may be the result of a corresponding event (see below). - The renderer produces the desired (complete or partial) XML representation at
(C, A, B). - If the contract implements
IRepresentableStateHashed, it obtainsstateHash()at blockB. - It signs a tuple like
(C, A, B, stateHash, hash(xml))off‑chain (e.g. with ECDSA).
This yields a compact, cryptographically anchored snapshot of the contract state.
To include the provided renderer, add a dependency on:
<dependency>
<groupId>net.finmath</groupId>
<artifactId>representable-contract-state</artifactId>
<version>${project.version}</version>
</dependency>
In your application create an instance evmXmlRenderer of net.finmath.smartcontracts.representablestate.xml.EvmXmlRenderer with a given chain (rpcUrl):
EvmXmlRenderer evmXmlRenderer = new EvmXmlRenderer(rpcUrl);
Then obtain an XML rendering of the XML-complete or partial state via
String xml = evmXmlRenderer.render(contractAddress, blockNumber, partId);
where
- a
nullvalue forblockNumberwill result in fetching the latest block, and - a
nullvalue forpartIdwill result in the XML-complete state (viastateXmlTemplate()), while any non-nullpartIdwill result in the partial state XML (viastatePartXmlTemplate(partId)).
The interpretation of partId can be decided by the contract.
For convenience EvmXmlRenderer provides the following methods:
String render(String contractAddress); // XML-complete state of given contract at latest block
String render(String contractAddress, BigInteger blockNumber); // XML-complete state of given contract at given block
String renderPart(String contractAddress, BigInteger partId); // XML of partial state for given partId at latest block
String renderPart(String contractAddress, BigInteger blockNumber, BigInteger partId); // XML of partial state for given partId at given block
Events “by reference” instead of “by value”
Motivation
Events are paid for by the transaction sender. Transporting large portions of state via event arguments may be expensive. Often such state is slowly evolving, repeating large portions of static data.
If the contract implements IXMLRepresentableState and/or IXMLRepresentableStatePart, it exposes a canonical, reproducible description of its internal state at each block. Events may choose to reference the state exposed through implementations of these interfaces.
In that case, the natural pattern is:
- Treat events as triggers.
- Put only a compact reference into the event.
- Off-chain consumers resolve that reference into a partial or full XML view at the block in which the event was emitted.
This is “by reference” instead of “by value”.
Recommended URI scheme
For the common case where the event refers to the same contract that emitted it, we recommend a compact URI scheme:
evmstate://self/part/<partId>[?key=<application-specific-key>]
where
self→ refer to the emitting contract.<partId>→ decimal or hexadecimal string of theuint256 partIdforstatePartXmlTemplate(partId).key(optional) → application-specific discriminator: index, timestamp, UUID, etc.
In that case the consuming service should conceptually replace the corresponding argument of the event with the rendering render(contractAddress, blockNumber, partId), where (contractAddress, blockNumber) are the values of the event and partId is taken from the URI.
Examples:
- Simple:
evmstate://self/part/1 - With a date key:
evmstate://self/part/1?key=2026-01-02
Summary
IXMLRepresentableStateexposes a canonical XML template; renderers turn it into a concrete XML representation at a fixed block.IRepresentableStateVersionedandIRepresentableStateHashedhelp with caching and integrity checks.IXMLRepresentableStatePartallows specialised partial views (e.g. settlement context).- Events SHOULD typically carry references into representable state (via URIs) rather than large by‑value payloads.
- The XML binding schema, including the optional array profile, provides a compact, generic way to bind arbitrary contract state into XML, ready to be consumed by existing XML tooling.
