When to provide metadata
Ethereum: Contract ABI
For Ethereum and EVM chains, provide the contract ABI to decode function calls. ABIs are passed as a map keyed by contract address, so wallets can supply one ABI per contract the transaction touches. For an end-to-end Ethereum integration walkthrough (supported networks, what’s auto-decoded, the rendered payload), see Ethereum for Wallets.gRPC example
Address casing
abi_mappings keys are 0x-prefixed contract addresses. The parser parses each key into a numeric address before registration, so lookup is case-insensitive: 0xdAC17... and 0xdac17... resolve to the same contract. Use a consistent casing convention (all lowercase or EIP-55 checksummed) across your ABI store to avoid duplicate entries for the same address.
ABI format
The ABI is standard Solidity ABI JSON:Specifying network
For EVM chains other than mainnet, setnetwork_id. The parser recognizes a broad set of values (Ethereum mainnet and testnets, BSC, Polygon, Avalanche, Gnosis, Celo, Fantom, Optimism, Arbitrum, Base, Blast, Mantle, World Chain, zkSync Era, Linea, Scroll, Zora, Unichain, and their testnet variants where applicable). The authoritative list is defined by network_id_to_chain_id in src/chain_parsers/visualsign-ethereum/src/networks.rs.
Built-in token metadata is preloaded only for ETHEREUM_MAINNET, POLYGON_MAINNET, ARBITRUM_MAINNET, OPTIMISM_MAINNET, and BASE_MAINNET. On other recognized chains, token symbol and decimal resolution is limited to the built-in registry; chain_metadata currently only accepts network_id and abi_mappings, so wallet-supplied token metadata is not yet supported. Always pass the actual network_id for the chain you’re parsing; the value overrides the transaction’s chain ID and reusing an unrelated one will mislabel the Network field.
Solana: Program IDL
For Solana programs, provide the Anchor IDL to decode instructions:gRPC example
Multiple programs
If your transaction interacts with multiple programs, use the IDL mappings:Supported IDL types
NEAR: Token Metadata
For NEAR Intents, provide a symbol and decimals for asset ids not already in the parser’s compiled-in seed table (see NEAR). Unlike Ethereum ABIs and Solana IDLs, entries are keyed by NEAR Intents asset id (e.g.nep141:wrap.near), not by a contract or program address.
gRPC example
Signed entries (optional)
ATokenMetadataEntry can carry an optional signature, so a wallet can trust an entry beyond the raw request itself. Unlike ABI/IDL signing (one curve per chain), the curve here is chosen per entry by origin_chain, since a NEAR Intents asset id can describe a NEAR-native asset or one bridged in from another chain:
Over the JSON/HTTP gateway the field carries these protobuf names, not the underlying numbers, and an unrecognized name is rejected rather than defaulted:
value’s bytes verbatim (the same convention as Abi.value/Idl.value), bound to both the asset id and the NEAR network so it cannot be replayed under a different asset or on the other network. Sign the exact bytes you put in value — the parser hashes them as supplied and never re-encodes them, so canonicalizing the JSON before signing produces a different prehash and the signature will not verify.
The network is part of the signed scope because a NEAR implicit account id (a 64-hex address) is the same string on mainnet and testnet. Sign against the canonical id — NEAR_MAINNET or NEAR_TESTNET — matching the network the request resolves to:
- The request’s
network_id, when it supplies one. An unrecognized value (testnetrather thanNEAR_TESTNET, say) fails the whole request rather than falling back. - Otherwise the network the parser was configured for, which is mainnet unless the deployment says otherwise.
Network field uses, so the network a signature is checked against is always the network the payload displays.
The resolved network must also agree with the accounts in the request. An account whose suffix contradicts it — .testnet under mainnet, or .near under testnet — fails the whole request rather than rendering under a network it does not belong to. This applies to a transaction’s own signer_id/receiver_id and to every rendered intents envelope’s signer_id/verifying_contract, on both the standalone and the on-chain batch path. Implicit (64-hex) accounts carry no suffix and are not constrained. So a wallet that sets network_id must set it to the network its accounts actually live on; supplying NEAR_TESTNET for a mainnet envelope is an error, not a relabelling.
An entry whose signature fails to verify is rejected outright, never silently downgraded to unsigned.
An entry the parser cannot attribute to a curator it recognizes — one carrying no signature, or one whose signature verifies under a key the deployment has not enrolled — is accepted only when both of these hold:
- The deployment runs a permissive trust posture.
parser_cliruns the strict posture and rejects both cases. - The asset is not already in the parser’s compiled-in token table. Unattributed metadata fills gaps for assets the parser doesn’t know; it cannot override a curated entry, since
decimalsscales the amount a signer sees.
unverified-token-metadata warning naming which of the two it was, so the signer is never told an entry was unsigned when it was signed by an unrecognized key. Which keys count as recognized is set per origin chain by the deployment — see NEAR.
See visualsign::signing for the exact prehash construction.
Library integration
When using the library directly (not gRPC), pass metadata viaVisualSignOptions. The Ethereum metadata shape mirrors the proto: a network_id and an abi_mappings map keyed by contract address.
Obtaining metadata
Ethereum ABIs
Solana IDLs
NEAR token metadata
Without metadata
If you don’t provide metadata for a contract interaction, the parser will:- Decode what it can (addresses, amounts in native token)
- Show the function selector (first 4 bytes) as hex
- Display raw calldata for unknown parameters
Next steps
- How Parsing Works: Understand the parsing pipeline
- Error Handling: Handle cases where parsing fails
- gRPC API Reference: Full request/response formats