Representable Contract State

an XML-based canonical view of EVM contract state


ERC-4804 web3:// XIncludes for EvmXmlRenderer

In ERC-4804, a URI scheme is defined for accessing on-chain data. The EvmXmlRenderer provides partial support for this scheme. This allows XML parts from contracts to be included.

The supported URI form is web3://[from-address@]<contract-address>:<chain-id>/<method>/<argument-0>/....

Demo

For the demo, we implement a function xml(uint256) returns (string) that returns an XML fragment for a given fragment ID (the function's uint256 argument). The XML template for the EvmXmlRenderer is:

<xi:include
    href="web3://0xRESOURCE_ADDRESS:CHAIN_ID/xml/0"
    parse="xml"
    evmstate:integrity="keccak256-0x..."/>

Note: The value after : is the EVM chain ID, not the JSON-RPC port. The JSON-RPC endpoint is supplied to the Java renderer separately.

The demo omits ?returns=(string). The renderer follows the ERC-6860 clarification of ERC-4804: an omitted return signature means one ABI-encoded bytes value; a Solidity string has the same dynamic ABI representation and is therefore accepted too. For XInclude, the decoded bytes/string are used directly as the XML resource instead of being converted to JSON.

Network examples

Network Renderer RPC URL example web3:// chain suffix
Local Anvil http://127.0.0.1:8545 :31337 when started with anvil --chain-id 31337
Polygon PoS mainnet https://polygon.drpc.org :137
Polygon Amoy https://polygon-amoy.drpc.org :80002

Examples:

web3://0xRESOURCE_ADDRESS:31337/xml/0
web3://0xRESOURCE_ADDRESS:137/xml/0
web3://0xRESOURCE_ADDRESS:80002/xml/0

The supplied renderer deliberately requires the URI chain ID to equal the chain ID reported by its configured RPC. This gives deterministic same-chain rendering. A cross-chain version would need an explicit, trusted chainId -> Web3j/RPC registry.

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. We also assume that curl, cat, tr, and sed are present.

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

We need three terminal sessions:

  1. one runs the blockchain (via anvil),
  2. one runs a Java service (EvmXmlRenderer) to display the contract state in a browser,
  3. one is used to issue commands to the blockchain.

The commands below use the standard local Anvil accounts. The private keys are public test keys and must never hold real assets or be used on a public network.


Terminal 1 — start the local chain

Use chain ID 31337 because the renderer process below is configured for 31337.

Run:

anvil --chain-id 31337

Leave Anvil running.


Terminal 2 — start the ERC-8100 renderer

The current renderer example uses release 2.5.0 and Java 21.

Run:

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

Leave the renderer running. It serves rendered contract state on port 8080.


Terminal 3 — deploy contracts and view EVM XML state

1. Download the Solidity demo

Open a shell. If desired, create a test directory and cd to that directory. Run the following commands:

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

2. Deploy the storage contract and the demo contract

Set RPC_URL and a private key:

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

Deploy the fragment store (a contract that stores XML fragments):

export STORE=$(
  forge create --use 0.8.24 --broadcast --root . --json src/Erc8100Web3IncludeDemo.sol:XmlFragmentStore \
    --rpc-url "$RPC_URL" \
    --private-key "$PRIVATE_KEY" \
  | tr -d '\r\n' \
  | sed -nE 's/.*"deployedTo"[[:space:]]*:[[:space:]]*"(0x[0-9a-fA-F]{40})".*/\1/p'
)

printf '\nStorage contract deployed at %s\n' "$STORE"

Deploy the ERC-8100-style template provider:

export DEMO=$(
  forge create  --use 0.8.24 --broadcast --root . --json src/Erc8100Web3IncludeDemo.sol:Erc8100Web3IncludeDemo \
    --rpc-url "$RPC_URL" \
    --private-key "$PRIVATE_KEY" \
    --constructor-args "$STORE" \
  | tr -d '\r\n' \
  | sed -nE 's/.*"deployedTo"[[:space:]]*:[[:space:]]*"(0x[0-9a-fA-F]{40})".*/\1/p'
)

printf '\nDemo contract deployed at %s\n' "$DEMO"

Inspect the resource and template:

cast call "$STORE" "resolveMode()(bytes32)" --rpc-url "$RPC_URL"
cast call "$STORE" "xml(uint256)(string)" 0 --rpc-url "$RPC_URL"
cast call "$STORE" "xmlHash(uint256)(bytes32)" 0 --rpc-url "$RPC_URL"
cast call "$DEMO" "stateXmlTemplate()(string)" --rpc-url "$RPC_URL"

The template contains URLs of the form:

web3://0xDEPLOYED_FRAGMENT_STORE:31337/xml/0
web3://0xDEPLOYED_FRAGMENT_STORE:31337/xml/1

3. Render from Java

web3 and w3 are included in XIncludeOptions.defaults(), so no extra option is required.

On macOS, open the live view with:

open "http://localhost:8080/xml?contract=$DEMO"

On Windows/Git Bash, open the live view with:

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

Expected structure after XInclude expansion:

<demo ...>
  <party>
    <name>Alice</name>
    <role>payer</role>
  </party>
  <terms>
    <currency>EUR</currency>
    <notional>1000000</notional>
  </terms>
</demo>

The root template, every web3:// include, and all evmstate:* calls are then evaluated at the same block number.

Polygon

Deploy both contracts using a Polygon RPC and a funded account. No Solidity change is needed: stateXmlTemplate() uses block.chainid, so it emits :137 on Polygon PoS and :80002 on Amoy.

Java examples:

EvmXmlRenderer polygon = new EvmXmlRenderer("https://polygon.drpc.org");
EvmXmlRenderer amoy = new EvmXmlRenderer("https://polygon-amoy.drpc.org");

Scope of the Java resolver

The implementation supports:

  • absolute web3:// and w3:// URLs;
  • hexadecimal contract addresses;
  • optional hexadecimal userinfo/from addresses;
  • same-chain calls at the renderer's fixed snapshot block;
  • ERC-4804/6860 auto and manual resolve modes;
  • scalar auto-mode arguments (bool, signed/unsigned integers, address, fixed/dynamic bytes, and an explicitly typed string);
  • one XML return value as bytes or string;
  • existing XInclude depth, size, cache, loop, secure-XML, and Keccak integrity controls.

It intentionally does not resolve ENS or other name-service names and does not automatically select an RPC for a different chain ID.