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://andw3://URLs; - hexadecimal contract addresses;
- optional hexadecimal
userinfo/fromaddresses; - 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
bytesorstring; - 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.
