Skip to content

How to read a native token from EVM

Goal: prove that a token minted through the SVM (see Issue a native token) is the same token an EVM contract sees — by reading its ERC-20 totalSupply() over eth_call.

This is the N-VM shared state in action: no bridge, no wrapper. See N-VM shared state for the why.

1. Derive the synthetic ERC-20 address

Every native mint is auto-registered at a deterministic EVM address:

erc20_address = sha256("erc20-addr:" || mint_id)[12..32]   // last 20 bytes
python
import hashlib
mint_id = bytes.fromhex("<your 32-byte mint id hex>")
addr = hashlib.sha256(b"erc20-addr:" + mint_id).digest()[12:32]
print("0x" + addr.hex())

2. eth_call the standard ERC-20 selectors

bash
RPC=https://testnet.acechain.io/rpc
ADDR=0x<erc20_address>

# totalSupply()  selector 0x18160ddd
curl -s -X POST $RPC -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","method":"eth_call","params":[{"to":"'$ADDR'","data":"0x18160ddd"},"latest"],"id":1}'

# decimals()     selector 0x313ce567
curl -s -X POST $RPC -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","method":"eth_call","params":[{"to":"'$ADDR'","data":"0x313ce567"},"latest"],"id":1}'

balanceOf(address) works too — selector 0x70a08231 followed by the 32-byte left-padded EVM address.

3. Worked example (verified on testnet)

A native mint of 1,000,000,000 tokens at 9 decimals returns:

totalSupply() → 0x0000000000000000000000000000000000000000000000000de0b6b3a7640000
              = 1000000000000000000  (1e18 base units = 1e9 × 10^9)  ✓
decimals()    → 0x0000000000000000000000000000000000000000000000000000000000000009
              = 9  ✓

The value read from EVM matches exactly what was minted from the SVM — one balance, two execution environments.

From a contract

Any Solidity contract can treat the address as a normal IERC20 and call transfer / transferFrom / balanceOf — see NativeTokenVault.sol.

Released under the Apache-2.0 License · Testnet · GitHub