Problem
assert/assertEq carry optional messages, but each backend already invents
its own failure surface:
- EVM: revert with no structured revert-reason encoding today.
- Solana: custom program error codes and log lines.
- NEAR: panic payload strings.
- Psy: circuit assertion index.
Backend/Solana/Client.lean); EVM emits ABI JSON;
NEAR, Psy, and Aleo emit nothing client-facing. The “one contract, many
chains” story is only real if the application developer gets one interface.
Summary
This RFC has two halves that share one schema layer:-
Portable runtime error model. Add an error id (
assertion_id+ optionaluser_code) at the portable IR level. Each target lowers it to a native encoding:Target Encoding evmrevertwith a compact ABI encoding of(assertion_id, user_code?)solana-sbpf-asmcustom program error code wasm-nearpanic payload with a prefixed compact code psy-dpnassertion index in circuit JSON -
Unified client schema. Generalize the Solana IDL into a target-neutral
ContractSpecJSON that describes entrypoints, types, accounts, and errors. Per-chain TS adapters (EVM ABI wrapper, Solana instruction builder, NEAR contract wrapper) are generated from this one schema.
Portable Runtime Error Model
IR-level representation
Everyassert/assertEq statement carries:
user_code as a string so contract authors can write readable
error names without bumping the binary assertion table.
Per-target encoding table
| Target | Native form | Decoding rule |
|---|---|---|
evm | revert(abi.encode(uint32 assertion_id, string user_code)) | Testkit parses revert data; user_code is UTF-8 |
solana-sbpf-asm | solana_program::program_error::ProgramError::Custom(assertion_id) | Custom error code; user_code emitted in IDL/client schema only |
wasm-near | panic!("PF:{assertion_id}:{user_code}") | Prefix PF: plus colon-separated fields for deterministic parsing |
psy-dpn | assertion index in .psy circuit metadata | Dargo preserves index; user_code in generated circuit docs |
assertion_id;
user_code then lives only in the client schema and deployment metadata.
Scenario vocabulary
Testkitexpect gains an error field:
expect.error, the runner asserts the step fails and
that the decoded error matches. If the step succeeds, the test fails.
FV pairing
Error semantics pair with FV-5 checked-arithmetic trap semantics: a checked arithmetic failure is just anotherassertion_id with a well-known code
(e.g. 0x0001 for overflow). The proof layer can state that no reachable path
raises a given error id.
Unified Client Schema
ContractSpec JSON
Generalize the Solana IDL into a target-neutral contract description:
accounts is populated per instruction. For EVM, the adapter
derives function selectors from entrypoint names and param types. For NEAR,
the adapter generates a wrapper around the exported functions.
Implementation boundary
The client-schema layer is implemented after testkit M3 because the testkit encoding adapters (selector/instruction/Borsh mapping) are the same logic and should be written once, then shared with client generation.Acceptance Criteria
- A
assertEqfailure in Counter produces the sameassertion_idanduser_codein EVM revert data, Solana custom error, and NEAR panic payload. - Testkit can assert
expect.error.assertion_idon all three Tier-0 targets. ContractSpecJSON is emitted for at least one existing module and used to regenerate the existing Solana TS client without behavioral change.- EVM ABI JSON and NEAR wrapper generation are sketched, even if not yet complete.
Milestones
- M1: Add
ErrorRefto the portable IRassert/assertEqconstructors and assign stable assertion ids during lowering. - M2: Implement per-target error encodings for EVM, Solana, and NEAR.
- M3: Extend testkit schema and harnesses with
expect.error; land together with RFC 0010’s budget schema change. - M4: Define
ContractSpecJSON schema and generate Solana IDL/client from it; add EVM and NEAR adapter sketches.
Non-goals
- This RFC does not define a new on-chain protocol or change consensus rules.
- It does not replace chain-native error systems; it wraps them.
- It does not implement the client layer before testkit M3.
Related
- RFC 0007: testkit scenario model.
- RFC 0010: budget schema change that should land in the same schema migration.
- Workstream 26: testkit M3.
- Workstream 33: runtime error model + client generation.