Representable Contract State
an XML-based canonical view of EVM contract state
NPM Package
The interfaces of the XML Representable Contract State is available via npm at @finmath.net/state.
IXMLRepresentableState
You find the interface IXMLRepresentableState in src/main/solidity.
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.23;
// ---------------------------------------- XML ------------------------------ */
/**
* @title XML Representable State interface
* @notice Contracts implementing this interface expose an XML template that can be rendered
* into a canonical XML representation of the contract state at a given block.
* @dev The XML binding schema and version are defined inside the XML itself (e.g. via
* namespaces or attributes). Snapshot consistency is achieved off-chain by evaluating
* all view calls against a single fixed block.
*
* In the context of this ERC, a contract that implements this interface and claims
* compliance as an "XML-complete" contract MUST ensure that the XML obtained from
* this template (together with its bindings) is sufficient to reconstruct the full
* contract state that is relevant for off-chain decisions (e.g. valuations,
* settlements) at a given block.
*
* Contracts that cannot make an XML-completeness guarantee SHOULD implement only
* IXMLRepresentableStatePart (and not this interface) if they wish to expose
* partial XML views of their state.
* @author Christian Fries
*/
interface IXMLRepresentableState {
/**
* @notice Returns the XML template string, using a dedicated namespace for bindings.
* @dev MUST return a well-formed XML 1.0 (or 1.1) document in UTF-8 encoding.
* Implementations SHOULD make this string independent of mutable contract state
* and environment variables, i.e., effectively constant.
*/
function stateXmlTemplate() external view returns (string memory);
}
/**
* @title XML Representable State (partial) interface
* @notice Optional extension exposing partial XML templates for selected views of the state.
* @dev The meaning of partId is contract-specific or defined by higher-level standards
* (e.g. “settlement context” for ERC-6123).
*
* Implementations of this interface alone are NOT required to be XML-complete:
* a contract may expose only partial views of its state without providing a
* canonical full XML representation via IXMLRepresentableState.
*/
interface IXMLRepresentableStatePart {
/**
* @notice Returns the XML template string for a particular partial state view.
* @dev MUST return a well-formed XML 1.0 (or 1.1) document in UTF-8 encoding.
* Implementations SHOULD make this string independent of mutable contract state
* and environment variables, i.e., effectively constant.
*
* @param partId Contract-specific identifier of the partial view.
*/
function statePartXmlTemplate(uint256 partId) external view returns (string memory);
}
// ---------------------------------------- JSON ------------------------------ */
/**
* @title JSON Representable State interface
* @notice Contracts implementing this interface expose a JSON template that can be rendered
* into a canonical JSON representation of the contract state at a given block.
* @dev The JSON binding schema and version are out of scope for this ERC. Snapshot consistency
* is achieved off-chain by evaluating all view calls against a single fixed block,
* analogous to the XML case.
*
* In the context of this ERC, a contract that implements this interface and claims
* compliance as a "JSON-complete" contract MUST ensure that the JSON obtained from
* this template (together with its bindings) is sufficient to reconstruct the full
* contract state that is relevant for off-chain decisions at a given block.
*
* Contracts that cannot make a JSON-completeness guarantee SHOULD implement only
* IJSONRepresentableStatePart (and not this interface) if they wish to expose
* partial JSON views of their state.
* @author Christian Fries
*/
interface IJSONRepresentableState {
/**
* @notice Returns the JSON template string.
* @dev MUST return a well-formed JSON document in UTF-8 encoding.
* Implementations SHOULD make this string independent of mutable contract state
* and environment variables, i.e., effectively constant.
*/
function stateJsonTemplate() external view returns (string memory);
}
/**
* @title JSON Representable State (partial) interface
* @notice Optional extension exposing partial JSON templates for selected views of the state.
* @dev The meaning of partId is contract-specific or defined by higher-level standards.
*
* Implementations of this interface alone are NOT required to be JSON-complete:
* a contract may expose only partial views of its state without providing a
* canonical full JSON representation via IJSONRepresentableState.
*/
interface IJSONRepresentableStatePart {
/**
* @notice Returns the JSON template string for a particular partial state view.
* @dev MUST return a well-formed JSON document in UTF-8 encoding.
* Implementations SHOULD make this string independent of mutable contract state
* and environment variables, i.e., effectively constant.
*
* @param partId Contract-specific identifier of the partial view.
*/
function statePartJsonTemplate(uint256 partId) external view returns (string memory);
}
// ---------------------------------------- State ------------------------------ */
/**
* @title Representable State (versioned) interface
* @notice Adds a monotonically increasing version of the representable state.
* This optional extension allows off-chain tools to cheaply detect whether
* the representation-relevant state has changed.
* @author Christian Fries
*/
interface IRepresentableStateVersioned {
/**
* @notice Monotonically increasing version of the representable state.
* @dev Implementations SHOULD increment this whenever any mutable state that participates
* in the representation changes. It MAY start at 0.
*
* Off-chain tools MAY use this to:
* - cache rendered XML/JSON and skip recomputation if the version is unchanged;
* - provide a simple ordering of state changes.
*/
function stateVersion() external view returns (uint256);
}
/**
* @title Representable State (hashed) interface
* @notice Exposes a hash of a canonical state tuple used for the representation.
* This optional extension allows off-chain tools to verify integrity of an
* externally provided representation against on-chain state.
* @author Christian Fries
*/
interface IRepresentableStateHashed {
/**
* @notice Hash of the canonical state tuple used for the representation.
* @dev Implementations MAY choose their own canonical encoding of state (e.g.,
* abi.encode of a tuple of all fields that are represented).
*
* This function is intended for off-chain integrity checks, for example:
* - parties can sign (chainId, contract, blockNumber, stateHash);
* - renderers can recompute the same hash from the values they used.
*
* It is RECOMMENDED that stateHash() is implemented as a pure/view
* function that computes the hash on the fly, instead of storing it in
* contract storage and updating it on every change.
*/
function stateHash() external view returns (bytes32);
}
// ------------------------------ Convenient Aggregations ------------------------------ */
/**
* @title XML Representable State (versioned) interface
* @notice Convenience interface combining XML template and versioned state.
*/
interface IXMLRepresentableStateVersioned is IXMLRepresentableState, IRepresentableStateVersioned {}
/**
* @title XML Representable State (hashed) interface
* @notice Convenience interface combining XML template and hashed state.
*/
interface IXMLRepresentableStateHashed is IXMLRepresentableState, IRepresentableStateHashed {}
/**
* @title XML Representable State (versioned + hashed) convenience interface
* @notice Convenience interface combining XML template and versioned/hashed state.
*/
interface IXMLRepresentableStateVersionedHashed is IXMLRepresentableState, IRepresentableStateVersioned, IRepresentableStateHashed {}
/**
* @title JSON Representable State (versioned) interface
* @notice Optional convenience interface combining JSON template and versioned state.
*/
interface IJSONRepresentableStateVersioned is IJSONRepresentableState, IRepresentableStateVersioned {}
/**
* @title JSON Representable State (hashed) interface
* @notice Optional convenience interface combining JSON template and hashed state.
*/
interface IJSONRepresentableStateHashed is IJSONRepresentableState, IRepresentableStateHashed {}
/**
* @title JSON Representable State (versioned + hashed) convenience interface
* @notice Optional convenience interface combining JSON template and versioned/hashed state.
*/
interface IJSONRepresentableStateVersionedHashed is IJSONRepresentableState, IRepresentableStateVersioned, IRepresentableStateHashed {}
Example Contract
The TestContract.sol (from src/main/solidity/examples) illustrates various XML bindings.
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.19;
import {
IXMLRepresentableStateVersionedHashed,
IXMLRepresentableState, // needed for @inheritdoc
IRepresentableStateVersioned, // needed for @inheritdoc
IRepresentableStateHashed // needed for @inheritdoc
} from "../IRepresentableState.sol";
/**
* @title TestContract
* @notice Contract to demonstrate and test data types, formats and array bindings
* for IRepresentableState.sol renderers.
*
* Fields are initialized with demo values in the constructor so that an off-chain renderer
* can immediately exercise different type/format combinations without any prior updates.
*/
contract TestContract is IXMLRepresentableStateVersionedHashed {
address public immutable owner;
// Unsigned integers
uint256 public valueUint; // e.g. 123456789
uint256 public valueMoney; // e.g. 123456 (scale 2 -> 1234.56)
uint256 public valueRateBP; // e.g. 25000 (scale 4 -> 2.5000%)
uint256 public valueHex; // e.g. 0xdeadbeef
uint256 public timestampDate; // UNIX ts -> iso8601-date
uint256 public timestampDateTime; // UNIX ts -> iso8601-datetime
// Signed integers
int256 public valueIntPos; // +123456
int256 public valueIntNeg; // -123456
// Address
address public exampleAddress;
// Bool
bool public flagTrue;
bool public flagFalse;
// String
string public textPlain;
// Bytes
bytes public dataBytes;
// Currency for multi-binding
string public currency;
// --- Arrays for array binding profile (Mode B) ---------------------------------------------
// e.g. coupon amounts (scale 2 -> 1000.00, 2500.00, ...)
int256[] public couponAmounts;
// matching labels for the coupons
string[] public couponLabels;
// XML state version (for versioned extension)
uint256 private _stateVersion;
constructor(address _owner) {
owner = _owner;
// Unsigned ints
valueUint = 123_456_789;
valueMoney = 123_456; // 1234.56 with scale=2
valueRateBP = 25_000; // 2.5000% with scale=4
valueHex = 0xDEADBEEF;
timestampDate = 1735689600; // 2025-01-01T00:00:00Z
timestampDateTime = 1735776000; // 2025-01-02T00:00:00Z
// Signed ints
valueIntPos = 123_456;
valueIntNeg = -123_456;
// Address
exampleAddress = _owner;
// Bools
flagTrue = true;
flagFalse = false;
// String
textPlain = "Hello, XML & EVM!";
// Bytes
dataBytes = hex"0102030405DEADBEEF0102030405DEADBEEF0102030405DEADBEEF";
// Currency for multi-binding
currency = "EUR";
// Arrays for array binding demo
couponAmounts.push(100_000); // 1000.00 with scale 2
couponAmounts.push(250_000); // 2500.00 with scale 2
couponAmounts.push(175_500); // 1755.00 with scale 2
couponLabels.push("Coupon 1");
couponLabels.push("Coupon 2");
couponLabels.push("Coupon 3");
_stateVersion = 1;
}
// --- IRepresentableState.sol ---
/// @inheritdoc IXMLRepresentableState
function stateXmlTemplate() external pure override returns (string memory) {
// Note: single quotes in XML to allow double quotes in solidity for a single string-block.
return
"<Contract xmlns='urn:example:contract'"
" xmlns:evmstate='urn:evm:state:1.0'"
" evmstate:chain-id=''"
" evmstate:contract-address=''"
" evmstate:block-number=''>"
"<TestContract xmlns='urn:example:format-showcase'>"
// ---- Unsigned Integers ----
"<UintRaw evmstate:call='valueUint()(uint256)' evmstate:format='integer'/>"
"<UintDecimal2 evmstate:call='valueMoney()(uint256)' evmstate:format='decimal' evmstate:scale='2'/>"
"<UintHex evmstate:call='valueHex()(uint256)' evmstate:format='hex'/>"
// Date/Datetime from UNIX timestamps (seconds since epoch)
"<Date evmstate:call='timestampDate()(uint256)' evmstate:format='iso8601-date'/>"
"<DateTime evmstate:call='timestampDateTime()(uint256)' evmstate:format='iso8601-datetime'/>"
// ---- Signed Integers ----
"<IntPos evmstate:call='valueIntPos()(int256)' evmstate:format='decimal'/>"
"<IntNeg evmstate:call='valueIntNeg()(int256)' evmstate:format='decimal'/>"
// ---- Address ----
"<ExampleAddress evmstate:call='exampleAddress()(address)' evmstate:format='address'/>"
// ---- Booleans ----
"<FlagTrue evmstate:call='flagTrue()(bool)' evmstate:format='boolean'/>"
"<FlagFalse evmstate:call='flagFalse()(bool)' evmstate:format='boolean'/>"
// ---- String ----
"<TextPlain evmstate:call='textPlain()(string)' evmstate:format='string'/>"
// ---- Bytes (hex + base64) ----
"<BytesHex evmstate:call='dataBytes()(bytes)' evmstate:format='hex'/>"
"<BytesBase64 evmstate:call='dataBytes()(bytes)' evmstate:format='base64'/>"
// ---- Multi-binding: amount as text, currency as attribute ----
"<Money"
" evmstate:calls='valueMoney()(uint256);currency()(string)'"
" evmstate:formats='decimal;string'"
" evmstate:scales='2;'" // 2 decimals for amount, no scaling for currency
" evmstate:targets=';currency'/>"
// ---- Array binding profile (Mode B): scalar arrays -> repeated rows ----
"<ArrayExamples>"
// int256[] -> repeated <Coupon> with decimal+scale
"<Coupons"
" evmstate:call='couponAmounts()(int256[])'"
" evmstate:item-element='Coupon'>"
"<Coupon"
" evmstate:item-field='0'"
" evmstate:format='decimal'"
" evmstate:scale='2'/>"
"</Coupons>"
// string[] -> repeated <Label> with plain string
"<CouponLabels"
" evmstate:call='couponLabels()(string[])'"
" evmstate:item-element='Label'>"
"<Label"
" evmstate:item-field='0'"
" evmstate:format='string'/>"
"</CouponLabels>"
"</ArrayExamples>"
"</TestContract>"
"</Contract>";
}
/// @inheritdoc IRepresentableStateVersioned
function stateVersion() external view override returns (uint256) {
return _stateVersion;
}
/// @inheritdoc IRepresentableStateHashed
function stateHash() external view override returns (bytes32) {
// Einfach alle relevanten Felder in die Hash-Basis aufnehmen
return keccak256(
abi.encode(
owner,
valueUint,
valueMoney,
valueRateBP,
valueHex,
timestampDate,
timestampDateTime,
valueIntPos,
valueIntNeg,
exampleAddress,
flagTrue,
flagFalse,
textPlain,
dataBytes,
currency,
couponAmounts,
couponLabels
)
);
}
}
License
The code is distributed under the Apache License version 2.0, unless otherwise explicitly stated.
