IDL and ABI
IDL (Interface Definition Language) files are the single source of truth for all contract interfaces. They define methods, parameters, return types, structs, and enums in a language-neutral format that drives code generation, ABI encoding, RPC exposure, and tooling discoverability.
Why IDL is the Source of Truth
Section titled “Why IDL is the Source of Truth”The IDL-first approach provides several critical guarantees:
| Benefit | Description |
|---|---|
| Language Neutrality | One definition generates Rust, Zig, TypeScript, Go, and C clients |
| Deterministic Selectors | Method selectors computed from signatures, not arbitrary constants |
| Type Safety | Parameter validation at build time and runtime |
| Discoverability | Tools can introspect contracts without source code |
| Versioning | Breaking changes detected through IDL comparison |
The IDL Pipeline
Section titled “The IDL Pipeline”┌─────────────────────────────────────────────────────────────────────────────┐│ IDL Source ││ (contracts/*.idl) │└───────────────────────────────────┬─────────────────────────────────────────┘ │ ┌─────────────────────┼─────────────────────┐ │ │ │ ▼ ▼ ▼ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │ Manifest │ │ Code Stubs │ │ On-Chain │ │ (selectors, │ │ (Rust, Zig, │ │ Storage │ │ types) │ │ TS, Go, C) │ │ (UTF-8 IDL) │ └───────┬────────┘ └───────┬────────┘ └───────┬────────┘ │ │ │ ▼ ▼ ▼ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │ JSON Codec │ │ Contract │ │ RPC Query │ │ (encode/ │ │ Binary │ │ contract_idl │ │ decode) │ │ (.elf) │ │ │ └────────────────┘ └────────────────┘ └────────────────┘IDL Syntax
Section titled “IDL Syntax”IDL files use a simple, declarative syntax:
namespace sft_v1;
/// A token balance with optional lock period.struct BalanceInfo { amount: u128; locked_until: Option<u64>;}
/// Transfer event emitted on every balance change.struct TransferEvent { from: Address; to: Address; amount: u128;}
/// Error codes returned by token operations.enum TokenError { InsufficientBalance(Unit); NotAuthorized(Unit); BlacklistedAddress(Address);}
interface SftV1 { /// Get the total token supply. fn total_supply() -> u128;
/// Get balance for an account. fn balance_of(account: Address) -> BalanceInfo;
/// Transfer tokens to another account. fn transfer(to: Address, amount: u128) -> bool;
/// Mint new tokens (owner only). fn mint(to: Address, amount: u128) -> bool;}Primitive Types
Section titled “Primitive Types”| Type | Description | JSON Encoding |
|---|---|---|
bool | Boolean | true / false |
u8..u64 | Unsigned integers | Number |
u128 | Large unsigned integer | String (decimal) |
i8..i64 | Signed integers | Number |
i128 | Large signed integer | String (decimal) |
String | UTF-8 string | String |
Address | 32-byte account address | Hex string (0x…) |
Composite Types
Section titled “Composite Types”| Type | Description | JSON Encoding |
|---|---|---|
Vec<T> | Variable-length array | [...] |
Option<T> | Optional value | null or value |
| Named struct | User-defined record | {field: value} |
| Named enum | Tagged union | {type: "variant", value: payload} |
Special Conventions
Section titled “Special Conventions”Unitstruct: Empty payload for enum variants- Documentation comments:
///lines become method/type docs - Namespace: Must match contract name for discoverability
Selector Computation
Section titled “Selector Computation”Method selectors are deterministically computed from the canonical signature:
selector = keccak256(signature)[0..4]signature = "Interface.method(param1:Type1,param2:Type2)"Canonical Type Names
Section titled “Canonical Type Names”| IDL Type | Canonical Form |
|---|---|
u128 | u128 |
Vec<Address> | vec<address> |
Option<String> | option<string> |
| Named struct | Struct name (e.g., BalanceInfo) |
Example
Section titled “Example”interface: SftV1method: transfer(to: Address, amount: u128)signature: "SftV1.transfer(to:address,amount:u128)"selector: keccak256(signature)[0..4] = 0x23b872ddCode Generation
Section titled “Code Generation”The idl-abi-gen crate generates language-specific code from IDL files.
Rust Contract Glue
Section titled “Rust Contract Glue”cargo run -p idl-abi-gen -- \ --idl contracts/sft_v1/sft_v1.idl \ --out-dir contracts/sft_v1/src \ --rust-contractGenerates abi.rs containing:
- Selector constants
- Request/response structs
- Encoding helpers
- Dispatch router
Client Stubs
Section titled “Client Stubs”# TypeScript clientashen idl codegen --idl sft_v1.idl --lang typescript
# Rust clientashen idl codegen --idl sft_v1.idl --lang rust
# Go clientashen idl codegen --idl sft_v1.idl --lang go
# C headerashen idl codegen --idl sft_v1.idl --lang cOn-Chain IDL Storage
Section titled “On-Chain IDL Storage”When a contract is deployed, its IDL is stored on-chain alongside the bytecode:
// During deploymentstore_contract_idl(&mut backend, &contract_address, idl_bytes);
// Query via RPClet result = client.contract_idl(&address)?;let idl_text = result.idl; // Option<String>Deploy Bundle Format
Section titled “Deploy Bundle Format”A deploy bundle contains three components:
┌───────────────────────────────────────┐│ Bundle Header (manifest length, etc) │├───────────────────────────────────────┤│ Manifest (JSON with metadata) │├───────────────────────────────────────┤│ IDL (UTF-8 text) │├───────────────────────────────────────┤│ ELF Binary (RISC-V contract code) │└───────────────────────────────────────┘Building a Deploy Bundle
Section titled “Building a Deploy Bundle”# Build contractcd contracts/sft_v1 && cargo build --release --target riscv64gc-unknown-none-elf
# Create bundle with IDLashen contract deploy \ --elf target/riscv64gc-unknown-none-elf/release/sft_v1 \ --idl contracts/sft_v1/sft_v1.idl \ --waitRPC Integration
Section titled “RPC Integration”The node RPC itself is defined by an IDL (src/rpc/node_rpc_v1.idl), enabling the same tooling to work for both:
- Contract interaction - Call contract methods via
view_call/tx_submit - Node API - Query chain state via
status,account,tx_by_hash, etc.
IDL-Aware RPC Client
Section titled “IDL-Aware RPC Client”The IdlRpcClient dynamically discovers and invokes RPC methods:
use crate::idl_rpc::IdlRpcClient;
let client = IdlRpcClient::new("http://localhost:3030", None)?;
// List all available methodsfor method in client.methods() { println!("{}: {}", method.qualified_name, method.doc.unwrap_or_default());}
// Get parameter template for a methodlet template = client.param_template("account")?;// {"address": "0x0000..."}
// Invoke method dynamicallylet result = client.call("account", json!({"address": "0x123..."}))?;Contract IDL Query
Section titled “Contract IDL Query”Any deployed contract’s IDL can be queried:
# CLIashen idl fetch --contract 0x1234...
# RPCcurl -X POST http://localhost:3030/v2/rpc \ -d '{"id":1,"method":"NodeRpcV1.contract_idl","params":{"address":"0x1234..."}}'JSON Encoding
Section titled “JSON Encoding”The idl-json-codec crate handles bidirectional conversion between JSON and Borsh:
Encoding (JSON → Calldata)
Section titled “Encoding (JSON → Calldata)”let abi = Abi::from_idl_path(&idl_path)?;
// Build calldata from JSON argumentslet calldata = abi.build_calldata( Some("SftV1"), "transfer", Some(&json!({"to": "0x123...", "amount": "1000"})), None)?;
// calldata = [selector (4 bytes)] + [borsh-encoded params]Decoding (Return Data → JSON)
Section titled “Decoding (Return Data → JSON)”// Decode return data using method signaturelet result = abi.decode_method_result( Some("SftV1"), "balance_of", &return_bytes)?;
// {"ok": {"amount": "1000", "locked_until": null}}// or// {"err": {"Code": {"code": 1, "data": null}}}Return Value ABI
Section titled “Return Value ABI”All contract returns use a standard envelope:
┌─────────────────────────────────────┐│ Tag (1 byte) ││ 0 = Error, 1 = Ok │├─────────────────────────────────────┤│ Payload (Borsh-encoded) ││ Tag=0: ContractError ││ Tag=1: Return type from IDL │└─────────────────────────────────────┘TUI Integration
Section titled “TUI Integration”The TUI (Terminal User Interface) uses IDLs for contract discoverability:
IDL Explorer
Section titled “IDL Explorer”Navigate and inspect registered IDLs:
┌─ IDL Explorer: sft_v1 ──────────────────────────────────────────────────────┐│ Namespace: sft_v1 ││ ││ [1] Overview [2] Structs [3] Methods ││ ││ ► total_supply() -> u128 ││ balance_of(account: Address) -> BalanceInfo ││ transfer(to: Address, amount: u128) -> bool ││ mint(to: Address, amount: u128) -> bool ││ burn(amount: u128) -> bool ││ approve(spender: Address, amount: u128) -> bool ││ ││ Press [enter] to invoke selected method │└─────────────────────────────────────────────────────────────────────────────┘Contract Discovery Flow
Section titled “Contract Discovery Flow”┌────────────────────────────────────────────────────────────────────────────┐│ User selects contract │└───────────────────────────────────┬────────────────────────────────────────┘ │ ┌──────────────▼──────────────┐ │ Check local IDL cache │ └──────────────┬──────────────┘ │ ┌─────────────────────┴─────────────────────┐ │ Cache hit │ Cache miss ▼ ▼ ┌────────────────┐ ┌────────────────┐ │ Load from │ │ Query RPC │ │ ~/.ashen/ │ │ contract_idl │ │ idl-cache/ │ │ │ └───────┬────────┘ └───────┬────────┘ │ │ │ ▼ │ ┌────────────────┐ │ │ Cache IDL │ │ │ locally │ │ └───────┬────────┘ │ │ └──────────────────┬──────────────────────┘ │ ▼ ┌────────────────┐ │ Parse IDL │ │ Display │ │ methods │ └────────────────┘Agent-Friendly Features
Section titled “Agent-Friendly Features”The IDL system enables AI agents to interact with contracts programmatically:
- Method Discovery: List all callable methods with types
- Parameter Templates: Generate valid JSON for any method
- Validation: Check arguments before submission
- Error Decoding: Structured error responses
# List methods (machine-readable)ashen tui contract methods --rpc http://localhost:3030 --contract 0x123... --json
# Get parameter templateashen contract inspect --idl sft_v1.idl | jq '.manifest.interfaces[].methods[]'IDL Validation and Compatibility
Section titled “IDL Validation and Compatibility”Validation
Section titled “Validation”Check an IDL for errors before deployment:
ashen idl validate --idl contracts/sft_v1/sft_v1.idlValidates:
- Syntax correctness
- Type references resolve
- No selector collisions
- Reserved word avoidance
Compatibility Checking
Section titled “Compatibility Checking”Detect breaking changes between IDL versions:
ashen idl compat \ --old-idl contracts/sft_v1/sft_v1_v1.idl \ --new-idl contracts/sft_v1/sft_v1_v2.idlBreaking Changes Detected:
- Method removal
- Method signature change
- Struct field removal/type change
- Enum variant removal
Non-Breaking Changes:
- New methods added
- New struct fields (if optional)
- New enum variants
CLI Reference
Section titled “CLI Reference”IDL Commands
Section titled “IDL Commands”| Command | Description |
|---|---|
ashen idl fetch | Fetch on-chain IDL for a contract |
ashen idl generate | Generate manifest JSON from IDL |
ashen idl validate | Validate IDL syntax and semantics |
ashen idl compat | Check compatibility between IDL versions |
ashen idl codegen | Generate client code (TS/Rust/Go/C) |
Contract Commands with IDL
Section titled “Contract Commands with IDL”| Command | Description |
|---|---|
ashen contract view --idl <path> | Call view method with IDL |
ashen contract call --idl <path> | Execute state-changing call |
ashen contract deploy --idl <path> | Deploy with on-chain IDL |
ashen contract inspect --idl <path> | Show IDL manifest |
TUI Configuration
Section titled “TUI Configuration”# Add local IDLashen tui config idls add --name sft_v1 --path ./contracts/sft_v1/sft_v1.idl
# List registered IDLsashen tui config idls list
# Remove IDLashen tui config idls remove --name sft_v1Best Practices
Section titled “Best Practices”IDL Design
Section titled “IDL Design”- Use descriptive names -
BalanceInfonotBI - Document everything -
///comments become API docs - Group related types - Keep request/response pairs together
- Version namespaces -
sft_v1,sft_v2for major changes - Keep structs small - Flatten deeply nested structures
Deployment
Section titled “Deployment”- Always include IDL - Enables discoverability
- Validate before deploy - Catch errors early
- Check compatibility - Before upgrading contracts
- Cache IDLs locally - Reduce RPC calls in development
Integration
Section titled “Integration”- Use typed clients - Generate code instead of manual encoding
- Validate inputs - Use
IdlRpcClient.validate_params() - Handle errors - Decode structured errors for debugging
- Log method calls - Include selector for traceability
Related
Section titled “Related”- Example Contracts - Reference implementations
- Ashen SDK - Contract development guide
- Execution - VM and gas model
- RPC Reference - CLI and RPC documentation