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 addresse.
Required Installations
If not already done:
- install
foundry(on macos viabrew install foundry) - install
jq(on macos viabrew install jq) - install
jbang(on macos viabrew 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.

Last updated: 2025‑11‑31
