Representable Contract State

an XML-based canonical view of EVM contract state


ERC-4804 web3:// XIncludes for EvmXmlRenderer

In ERC-4804 an URI scheme is defined to access on-chain data. The EvmXmlRenderer (partially) supports this scheme. This allows to include XML parts from contracts.

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 uint256). The xml template for the EvmXmlRenderer the 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.

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

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 both live views with:

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

On Windows/GitBash, open both live views 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 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.