logo

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.