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

Required Installations

You need foundry (for the chain tooling) and jbang (to start a web service with the renderer). For installation instructions see the setup page. Apart from this we assume that cat, tr and sed are present.

The following commands should be run in a shell (macOS Terminal, Windows GitBash). You may use the copy button and just paste them into the shell.


Step-by-Step

1. Launch a local chain for testing

Run (recommended):

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 (in a new shell):

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

Example 1: A Small Contract with State Changes

3. Deploy a small contract for testing

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

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

3.1. Create the file MinimalInstrument.sol

Run the following 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 (1/100 of currency)
    string  public currency;      // currency
    uint256 public maturityDate;  // date in epoch (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;'"     /* 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>"
            "</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")
CONTRACT_PATH=.
CONTRACT_NAME=MinimalInstrument
NOTIONAL=100000000
MATURITY=1735689600

CONTRACT_ADDRESS=$(
  forge create --use 0.8.19 --broadcast --root . --json "$CONTRACT_PATH/$CONTRACT_NAME.sol:$CONTRACT_NAME" --rpc-url "$RPC_URL" --private-key "$PRIVATE_KEY" \
    --constructor-args "$OWNER" "$NOTIONAL" "$MATURITY" \
  | tr -d '\r\n' \
  | sed -nE 's/.*"deployedTo"[[:space:]]*:[[:space:]]*"(0x[0-9a-fA-F]{40})".*/\1/p'
)

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"

On Windows/GitBash, run:

start "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 by reloading the page.

6. Result

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

Screenshot representable-contract-state-minimal-instrument


Example 2: Partial State Views and Array Bindings

The following example renders a settle-to-market interest rate swap that reports its settlement history (which is internally an array of a struct).

7. Download IRepresentableState.sol and examples/InterestRateSwapSettleToMarket.sol

curl --create-dirs -L 'https://gitlab.com/finmath/representable-contract-state/-/raw/main/representable-contract-state-web/src/main/solidity/IRepresentableState.sol' -o IRepresentableState.sol

curl --create-dirs -L 'https://gitlab.com/finmath/representable-contract-state/-/raw/main/representable-contract-state-web/src/main/solidity/examples/InterestRateSwapSettleToMarket.sol' -o examples/InterestRateSwapSettleToMarket.sol

8. Build and deploy InterestRateSwapSettleToMarket

export RPC_URL="http://127.0.0.1:8545"
export PRIVATE_KEY="0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"

OWNER=$(cast wallet address --private-key "$PRIVATE_KEY")
CONTRACT_PATH=./examples
CONTRACT_NAME=InterestRateSwapSettleToMarket

CONTRACT_ADDRESS=$(
  forge create --use 0.8.19 --broadcast --root . --json "$CONTRACT_PATH/$CONTRACT_NAME.sol:$CONTRACT_NAME" --rpc-url "$RPC_URL" --private-key "$PRIVATE_KEY" \
    --constructor-args "$OWNER" "$NOTIONAL" "$MATURITY" \
  | tr -d '\r\n' \
  | sed -nE 's/.*"deployedTo"[[:space:]]*:[[:space:]]*"(0x[0-9a-fA-F]{40})".*/\1/p'
)

echo "Contract deployed at $CONTRACT_ADDRESS"

9. Open the contract state in the browser

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"

On Windows/GitBash, run:

start "http://localhost:8080/xml?contract=$CONTRACT_ADDRESS"

(scroll down to view the settlements)

10. Perform a settlement using performSettlement(uint256 time, int256 value)

The following will create one settlement entry in the contract:

SETTLEMENT_TIME=$(date +%s)
# some (not so random) value
SETTLEMENT_VALUE=$(((SETTLEMENT_TIME % 11 - 5) * 10000))

cast send "$CONTRACT_ADDRESS" \
  "performSettlement(uint256,int256)" "$SETTLEMENT_TIME" "$SETTLEMENT_VALUE" \
  --rpc-url "$RPC_URL" --private-key "$PRIVATE_KEY" --legacy --gas-price 1gwei --gas-limit 200000

(reload the contract view)

11. Open the partial state view for the settlement information

In a browser, navigate to: http://localhost:8080/xml/part?partid=1&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/part?partid=1&contract=$CONTRACT_ADDRESS"

On Windows/GitBash, run:

start "http://localhost:8080/xml/part?partid=1&contract=$CONTRACT_ADDRESS"

Now:

  • perform another settlement (step 10), and
  • reload the partial state view (step 11).

12. Result

If all went well, you should see the settlement history rendered from the contract’s template.

representable-contract-state-interestrate-swap.png


Remark: All the XML you saw was defined inside the contracts. All values in the XMLs were derived from bindings to contract variables. The renderer is generic and does not know anything about the business XML schema or the internal storage layout.

Last updated: 2025‑11‑31