SDK Reference
Ashen provides SDKs for writing smart contracts in Zig and Rust.
Contract SDK Comparison
Section titled “Contract SDK Comparison”| Feature | Zig SDK | Rust SDK |
|---|---|---|
| Location | contracts/ashen-sdk/ | crates/contract-sdk/ |
| Use Case | Smart contracts | Smart contracts |
| Storage Helpers | storage, collections | Item, Map, Set, StorageVec |
| Safe Math | sdk.math, sdk.guards | Manual |
| Events | sdk.events (typed) | Host::emit_log |
| Reentrancy Guards | sdk.guards | Manual |
| Cross-Contract Calls | sdk.call | Host::call, Host::static_call |
| Codegen | IDL → Zig stubs | IDL → Rust stubs |
Zig SDK
Section titled “Zig SDK”The Zig SDK (contracts/ashen-sdk/) provides the most complete developer experience for smart contracts.
const sdk = @import("ashen-sdk");
export fn _start(calldata_ptr: [*]const u8, calldata_len: usize) sdk.ByteSlice { sdk.heap.reset(); const calldata = if (calldata_len == 0) &[_]u8{} else calldata_ptr[0..calldata_len]; // Dispatch to your methods... return sdk.ByteSlice.from(result);}
pub const panic = sdk.panic;const storage = sdk.storage;
// Raw bytesconst value = storage.read(key);storage.write(key, data);
// Typed u128const balance = storage.readU128(key);storage.writeU128(key, amount);const events = sdk.events;
// Define event signatureconst Transfer = events.define("Transfer(address,address,uint256)");
// Emit with indexed topicsTransfer.emit2(from, to, events.toTopicU128(amount), &[_]u8{});const math = sdk.math;const guards = sdk.guards;
// Safe arithmeticconst sum = guards.safeAdd(u128, a, b) catch return error;const diff = guards.safeSub(u128, a, b) catch return error;
// Fixed-point mathconst fee = math.bpsMul(amount, 30); // 0.3%const shares = math.isqrt(product);
// Reentrancy protectiontry guards.enterNonReentrant();defer guards.exitNonReentrant();Zig SDK Modules
Section titled “Zig SDK Modules”| Module | Import | Purpose |
|---|---|---|
heap | sdk.heap | Bump allocator: reset(), allocSlice(), allocator |
storage | sdk.storage | read(), write(), readU128(), writeU128() |
context | sdk.context | caller(), origin(), blockHeight(), value() |
crypto | sdk.crypto | keccak256(), sha256(), blake3() |
call | sdk.call | Cross-contract: call(), staticCall() |
events | sdk.events | Typed events with indexed topics |
guards | sdk.guards | Reentrancy protection, safe arithmetic |
math | sdk.math | WAD/RAY/BPS fixed-point, isqrt(), min(), max() |
Rust SDK
Section titled “Rust SDK”The Rust SDK (crates/contract-sdk/) provides low-level control for contracts requiring Rust-specific features.
Crate Architecture
Section titled “Crate Architecture”| Crate | Purpose |
|---|---|
contract-rt | Minimal runtime: entrypoint, panic handler, allocator |
contract-sdk | Developer API: Host struct, storage, ABI helpers |
Host API
Section titled “Host API”use contract_sdk::{Host, Address, ContractErrorV1};
impl Host { // Storage pub fn storage_read(key: &[u8]) -> Result<Option<Vec<u8>>, HostError>; pub fn storage_write(key: &[u8], value: &[u8]) -> Result<(), HostError>;
// Context pub fn caller() -> Address; pub fn origin() -> Address; pub fn self_address() -> Address; pub fn block_number() -> u64; pub fn block_timestamp() -> u64;
// Logs pub fn emit_log(topics: &[[u8; 32]], data: &[u8]) -> Result<(), HostError>;
// Cross-contract calls pub fn call<T: BorshDeserialize>( to: &Address, value: u64, gas: u64, calldata: &[u8] ) -> Result<T, CallError>;
pub fn static_call<T: BorshDeserialize>( to: &Address, gas: u64, calldata: &[u8] ) -> Result<T, CallError>;
// Crypto precompiles pub fn keccak256(input: &[u8]) -> Result<[u8; 32], HostError>; pub fn sha2_256(input: &[u8]) -> Result<[u8; 32], HostError>; pub fn blake3(input: &[u8]) -> Result<[u8; 32], HostError>;}Storage Collections
Section titled “Storage Collections”use contract_sdk::{Item, Map, Set, StorageVec, define_storage};
define_storage! { namespace: "my_contract", pub struct Storage { pub counter: Item<u64>, pub owner: Item<[u8; 32]>, pub balances: Map<[u8; 32], u128>, pub approved: Set<[u8; 32]>, pub history: StorageVec<Event>, }}Error Handling
Section titled “Error Handling”use contract_sdk::{ContractErrorV1, require};
// Standardized errorspub enum ContractErrorV1 { Code { code: u32, data: Option<Vec<u8>> }, Other { message: String }, // max 1024 bytes}
// Require macrorequire!(amount > 0, ERR_ZERO_AMOUNT);require!(caller == owner, ERR_UNAUTHORIZED);ABI Encoding
Section titled “ABI Encoding”All contracts use: calldata = selector (4 bytes) || borsh(args)
Returns: borsh(Result<T, ContractErrorV1>)
use contract_sdk::{parse_calldata_v1, encode_result_v1, SELECTOR_BYTES_V1};
// Parse incoming calllet (selector, args_bytes) = parse_calldata_v1(calldata)?;
// Encode responselet response = encode_result_v1(&Ok(return_value));Client SDK (TypeScript)
Section titled “Client SDK (TypeScript)”The TypeScript SDK (packages/ashen-sdk-ts/) is for client-side applications that interact with the chain—not for writing smart contracts. Use it for:
- Building frontends and dApps
- Agent automation and scripting
- Transaction signing and submission
- Reading chain state via RPC
Features
Section titled “Features”| Feature | Description |
|---|---|
| RPC Client | Full JSON-RPC interface with retries and error handling |
| Borsh Encoding | Encode/decode transaction arguments and return values |
| ed25519 Signing | Sign transactions with ed25519 keypairs |
| IDL Codegen | Generate type-safe contract bindings from IDL |
| Light Client | Verify state proofs without trusting the RPC |
import { AshenClient, Keypair } from 'ashen-sdk-ts';
// Connect to RPCconst client = new AshenClient('https://rpc.ashen.sh');
// Read stateconst balance = await client.getBalance(address);const nonce = await client.getNonce(address);
// Sign and submit a transactionconst keypair = Keypair.fromSecretKey(secretKey);const tx = await client.buildTransaction({ to: contractAddress, data: encodedCalldata, nonce,});const signedTx = keypair.sign(tx);const hash = await client.submitTransaction(signedTx);
// Wait for inclusionconst receipt = await client.waitForTransaction(hash);Codegen
Section titled “Codegen”Generate TypeScript bindings from IDL for type-safe contract interaction:
cargo run -p idl-abi-gen -- \ --idl contracts/sft_v1/sft_v1.idl \ --out-dir ./generated \ --typescriptThis generates typed methods for each contract function:
import { SftV1Client } from './generated/sft_v1';
const sft = new SftV1Client(client, contractAddress);const totalSupply = await sft.totalSupply();await sft.mint({ to: recipient, amount: 1000n });IDL Code Generation
Section titled “IDL Code Generation”The idl-abi-gen crate generates type-safe bindings for all languages.
# Zig stubs + dispatchercargo run -p idl-abi-gen -- \ --idl contracts/mycontract/mycontract.idl \ --out-dir contracts/mycontract/src \ --zig-stubs
# Rust bindingscargo run -p idl-abi-gen -- \ --idl contracts/mycontract/mycontract.idl \ --out-dir contracts/mycontract/src \ --rust-contract
# TypeScript clientcargo run -p idl-abi-gen -- \ --idl contracts/mycontract/mycontract.idl \ --out-dir ./generated \ --typescriptSelector Derivation
Section titled “Selector Derivation”Selectors are 4 bytes derived from the method signature:
selector = blake3(signature_string)[0..4]The signature includes method name and canonicalized argument types. Collisions within a contract interface cause a hard failure at codegen time.