logo

Representable Contract State

an XML-based canonical view of EVM contract state


What you’ll do

  • Start a local test chain (anvil or besu)
  • Start an EvmXmlRenderer web-server connected to the local chain.
  • Deploy a small demo contract (using foundry).
  • Render the representable state of the contract at the deployed addresse.

Required Installations

If not already done:

  • install foundry (on macos via brew install foundry)
  • install jq (on macos via brew install jq)
  • install jbang (on macos via brew install jbang)

If you are on Windows use GitBash (GitBash is part of Git on Windows: install git)


Step-by-Step

1. Launch a local chain for testing

Run (recomended)

anvil

or

DEPLOYER=0x70997970C51812dc3A010C7d01b50e0d17dc79C8
docker run --rm -it --name besu -p 8545:8545 -p 8546:8546 hyperledger/besu:latest --network=dev --miner-enabled=true --miner-coinbase $DEPLOYER --rpc-http-enabled=true --rpc-http-host=0.0.0.0 --rpc-http-port=8545 --rpc-http-api=ETH,NET,WEB3,TXPOOL,TRACE,DEBUG --host-allowlist="*" --logging=INFO

2. Launch the EvmXmlRenderer Web-Server

Run

jbang -Dethereum.rpcUrl=http://127.0.0.1:8545 -Dethereum.chainId=1337 net.finmath:representable-contract-state-web:1.0.7

3. Deploy a Small Contract for Testing

We create a small contract that will expose its internal state via an IXMLRepresentableState. State can be updated via a small update function.

The EvmXmlRenderer (which only needs to know the addres of the contract) will then render the internal state.

3.1. Create the File MinimalInstrument.sol

Run the follwoing in your terminal

cat > MinimalInstrument.sol <<'EOF'
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.19;

// Interface (repeated here for convenience to have a single file
interface IXMLRepresentableState { function stateXmlTemplate() external view returns (string memory); }
interface IRepresentableStateVersioned { function stateVersion() external view returns (uint256); }
interface IRepresentableStateHashed { function stateHash() external view returns (bytes32); }
interface IXMLRepresentableStateVersionedHashed is IXMLRepresentableState, IRepresentableStateVersioned, IRepresentableStateHashed {}

/**
 * @title Example XML-representable contract
 */
contract MinimalInstrument is IXMLRepresentableStateVersionedHashed {
    address public owner;

    uint256 public notional;      // notional in cent (per 1/100 of currency)
    string  public currency;      // currency
    uint256 public maturityDate;  // date in ecpoch (sec. since 1.1.1970 00:00:00 UTC)
    bool    public active;

    uint256 private _stateVersion;

    event Updated(address indexed updater, uint256 newNotional, uint256 newMaturity, bool newActive);

    constructor(address _owner, uint256 _notional, uint256 _maturityDate) {
        owner = _owner;
        notional = _notional;
        currency = "EUR";
        maturityDate = _maturityDate;
        active = true;
        _stateVersion = 1;
    }

    function update(uint256 _notional, uint256 _maturityDate, bool _active) external {
        require(msg.sender == owner, "not owner");
        notional = _notional;
        maturityDate = _maturityDate;
        active = _active;
        _stateVersion += 1;
        emit Updated(msg.sender, _notional, _maturityDate, _active);
    }

    function stateXmlTemplate() external pure override returns (string memory) {
        return
            "<Contract xmlns='urn:example:contract' xmlns:evmstate='urn:evm:state:1.0'>"
                "<Instrument xmlns='urn:example:format-showcase'>"
                    "<Owner evmstate:call='owner()(address)' evmstate:format='address'/>"
                    "<Notional"
                        " evmstate:calls='notional()(uint256);currency()(string)'"
                        " evmstate:formats='decimal;string'"
                        " evmstate:scales='2;'"
                        " evmstate:targets=';currency'/>"
                    "<MaturityDate evmstate:call='maturityDate()(uint256)' evmstate:format='iso8601-date'/>"
                    "<Active evmstate:call='active()(bool)' evmstate:format='boolean'/>"
                "</Instrument>"
            "</Contract>";
    }

    function stateVersion() external view override returns (uint256) {
        return _stateVersion;
    }

    function stateHash() external view override returns (bytes32) {
        return keccak256(abi.encode(owner, notional, maturityDate, active));
    }
}
EOF

3.2. Build and Deploy the Contract MinimalInstrument

Run

export RPC_URL="http://127.0.0.1:8545"
export PRIVATE_KEY="0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"
OWNER=$(cast wallet address --private-key "$PRIVATE_KEY")
NOTIONAL=100000000
MATURITY=1735689600

CONTRACT_ADDRESS=$(forge create --use 0.8.19 --broadcast --json MinimalInstrument.sol:MinimalInstrument --rpc-url "$RPC_URL"  --private-key "$PRIVATE_KEY"  --constructor-args "$OWNER" "$NOTIONAL" "$MATURITY" | jq -r '.deployedTo')

echo "Contract deployed at $CONTRACT_ADDRESS"

4. Check the State of the Deployed Contract

In a Browser, navigate to http://localhost:8080/xml?contract=$CONTRACT_ADDRESS , where $CONTRACT_ADDRESS is replaced by the address of the contract (starting with 0x). On macos, run

open http://localhost:8080/xml?contract=$CONTRACT_ADDRESS

5. Change the Internal State of the Contract

Run

NOTIONAL=200000000
MATURITY=1759200000

cast send "$CONTRACT_ADDRESS" "update(uint256,uint256,bool)" \
  $NOTIONAL $MATURITY true \
  --rpc-url "$RPC_URL" \
  --private-key "$PRIVATE_KEY" \
  --legacy \
  --gas-price 1gwei \
  --gas-limit 200000

(adapt the values if you like).

👉 now re-check the state of the contract, reloading the page.

Result

If all went well, you should see the state rendered from the contract's template.

representable-contract-state-minimal-instrument.png


Last updated: 2025‑11‑31