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

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.

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
