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: -
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
Targets that cannot encode the full pair must at least encode
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.