Skip to content

SDK Reference

Ashen provides SDKs for writing smart contracts in Zig and Rust.

FeatureZig SDKRust SDK
Locationcontracts/ashen-sdk/crates/contract-sdk/
Use CaseSmart contractsSmart contracts
Storage Helpersstorage, collectionsItem, Map, Set, StorageVec
Safe Mathsdk.math, sdk.guardsManual
Eventssdk.events (typed)Host::emit_log
Reentrancy Guardssdk.guardsManual
Cross-Contract Callssdk.callHost::call, Host::static_call
CodegenIDL → Zig stubsIDL → Rust stubs

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;
ModuleImportPurpose
heapsdk.heapBump allocator: reset(), allocSlice(), allocator
storagesdk.storageread(), write(), readU128(), writeU128()
contextsdk.contextcaller(), origin(), blockHeight(), value()
cryptosdk.cryptokeccak256(), sha256(), blake3()
callsdk.callCross-contract: call(), staticCall()
eventssdk.eventsTyped events with indexed topics
guardssdk.guardsReentrancy protection, safe arithmetic
mathsdk.mathWAD/RAY/BPS fixed-point, isqrt(), min(), max()

The Rust SDK (crates/contract-sdk/) provides low-level control for contracts requiring Rust-specific features.

CratePurpose
contract-rtMinimal runtime: entrypoint, panic handler, allocator
contract-sdkDeveloper API: Host struct, storage, ABI helpers
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>;
}
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>,
}
}
use contract_sdk::{ContractErrorV1, require};
// Standardized errors
pub enum ContractErrorV1 {
Code { code: u32, data: Option<Vec<u8>> },
Other { message: String }, // max 1024 bytes
}
// Require macro
require!(amount > 0, ERR_ZERO_AMOUNT);
require!(caller == owner, ERR_UNAUTHORIZED);

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 call
let (selector, args_bytes) = parse_calldata_v1(calldata)?;
// Encode response
let response = encode_result_v1(&Ok(return_value));


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
FeatureDescription
RPC ClientFull JSON-RPC interface with retries and error handling
Borsh EncodingEncode/decode transaction arguments and return values
ed25519 SigningSign transactions with ed25519 keypairs
IDL CodegenGenerate type-safe contract bindings from IDL
Light ClientVerify state proofs without trusting the RPC
import { AshenClient, Keypair } from 'ashen-sdk-ts';
// Connect to RPC
const client = new AshenClient('https://rpc.ashen.sh');
// Read state
const balance = await client.getBalance(address);
const nonce = await client.getNonce(address);
// Sign and submit a transaction
const 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 inclusion
const receipt = await client.waitForTransaction(hash);

Generate TypeScript bindings from IDL for type-safe contract interaction:

Terminal window
cargo run -p idl-abi-gen -- \
--idl contracts/sft_v1/sft_v1.idl \
--out-dir ./generated \
--typescript

This 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 });

The idl-abi-gen crate generates type-safe bindings for all languages.

Terminal window
# Zig stubs + dispatcher
cargo run -p idl-abi-gen -- \
--idl contracts/mycontract/mycontract.idl \
--out-dir contracts/mycontract/src \
--zig-stubs
# Rust bindings
cargo run -p idl-abi-gen -- \
--idl contracts/mycontract/mycontract.idl \
--out-dir contracts/mycontract/src \
--rust-contract
# TypeScript client
cargo run -p idl-abi-gen -- \
--idl contracts/mycontract/mycontract.idl \
--out-dir ./generated \
--typescript

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.