ownable-std 0.6.1

utils library for eqty ownable contracts
Documentation

ownable-std

ownable-std is the shared Rust standard library for Ownables contracts.

It provides:

  • common types/utilities used by Ownables contracts
  • optional message-shaping proc macros (ownable-std-macros)
  • a stable Host ABI v1 for wasm runtime calls that does not depend on wasm-bindgen JS glue compatibility

Install

[dependencies]
ownable-std = "0.4.0"

macros is enabled by default. To disable proc macros:

[dependencies]
ownable-std = { version = "0.4.0", default-features = false }

ownable-std has no wasm-bindgen or js-sys dependency in runtime contract usage.

Host ABI v1

Constants:

  • ownable_std::abi::HOST_ABI_VERSION = "1"
  • ownable_std::abi::HOST_ABI_MANIFEST_FIELD = "ownablesAbi"
  • ownable_std::abi::HOST_ABI_WIRE_FORMAT = "cbor"
  • ownable_std::abi::HOST_ABI_WIRE_FORMAT_MANIFEST_FIELD = "wireFormat"

Stable exports:

  • ownable_alloc(len: u32) -> u32
  • ownable_free(ptr: u32, len: u32)
  • ownable_instantiate(ptr: u32, len: u32) -> u64
  • ownable_execute(ptr: u32, len: u32) -> u64
  • ownable_query(ptr: u32, len: u32) -> u64
  • ownable_external_event(ptr: u32, len: u32) -> u64

Return strategy:

  • entrypoints return packed u64
  • low 32 bits = output pointer
  • high 32 bits = output length

Wire format:

  • input: CBOR bytes
  • output: CBOR envelope bytes

Request Schemas (Per Call)

At the ABI transport layer, all four calls use the same schema:

  • instantiate: CBOR document bytes
  • execute: CBOR document bytes
  • query: CBOR document bytes
  • external_event: CBOR document bytes

Required ABI-level keys:

  • none (the full request shape is contract-defined)

Recommended convention for cross-contract consistency:

{
  abi_version: "1",
  payload: <contract-defined value>
}

If you use the convention above:

  • abi_version: required string, must be "1"
  • payload: required contract-defined CBOR value

Response envelope schema (decoded structure):

{
  success: bool,
  payload: bytes,
  error_code: string | null,
  error_message: string | null
}

Contract Usage

Use the ownable_host_abi_v1! macro to export all required ABI symbols.

use ownable_std::abi::HostAbiError;
use ownable_std::ownable_host_abi_v1;

fn instantiate_handler(input: &[u8]) -> Result<Vec<u8>, HostAbiError> {
    let v: serde_cbor::Value = serde_cbor::from_slice(input)?;
    serde_cbor::to_vec(&v).map_err(HostAbiError::from)
}

fn execute_handler(input: &[u8]) -> Result<Vec<u8>, HostAbiError> {
    let v: serde_cbor::Value = serde_cbor::from_slice(input)?;
    serde_cbor::to_vec(&v).map_err(HostAbiError::from)
}

fn query_handler(input: &[u8]) -> Result<Vec<u8>, HostAbiError> {
    let v: serde_cbor::Value = serde_cbor::from_slice(input)?;
    serde_cbor::to_vec(&v).map_err(HostAbiError::from)
}

fn external_event_handler(input: &[u8]) -> Result<Vec<u8>, HostAbiError> {
    let v: serde_cbor::Value = serde_cbor::from_slice(input)?;
    serde_cbor::to_vec(&v).map_err(HostAbiError::from)
}

ownable_host_abi_v1!(
    instantiate = instantiate_handler,
    execute = execute_handler,
    query = query_handler,
    external_event = external_event_handler,
);

Handler signature expected by the macro:

fn handler(input: &[u8]) -> Result<Vec<u8>, E>
where
    E: Into<ownable_std::abi::HostAbiError>;

Host Runtime Call Flow

For each call (instantiate, execute, query, external_event):

  1. Serialize request object to CBOR bytes.
  2. Call ownable_alloc(len).
  3. Write bytes into wasm memory at the returned pointer.
  4. Call the selected ownable_* entrypoint with (ptr, len).
  5. Unpack (out_ptr, out_len) from returned u64.
  6. Read out_len bytes at out_ptr.
  7. Parse CBOR envelope and check success.
  8. Call ownable_free(out_ptr, out_len).

Memory Ownership Rules

  • ownable_alloc(len) allocates wasm-side memory for host writes.
  • Host owns writing input bytes into this allocated buffer.
  • Entrypoints consume input bytes immediately and copy them internally; host may treat input buffer as transient.
  • Entrypoints allocate output envelope bytes and return (ptr,len) packed in u64.
  • Host must call ownable_free(out_ptr, out_len) exactly once for every non-zero output buffer.
  • ptr=0,len=0 means empty output.
  • Passing ptr=0,len>0 to entrypoints yields structured ABI error (INVALID_POINTER), not panic.
  • This crate does not enforce a hard max payload size; host/runtime should enforce practical limits.

Error Code Catalog

Current ABI-level error_code values:

  • INVALID_POINTER: null pointer with non-zero length input
  • INVALID_CBOR: request bytes failed CBOR parsing in contract handler
  • SERIALIZATION_FAILED: ABI envelope serialization failed
  • HANDLER_PANIC: contract handler panicked; panic is converted to structured error

Handlers may return additional domain-specific error codes via HostAbiError::with_code.

Non-UTF8 Behavior

Non-UTF8 input must not panic.

Expected behavior:

  • handler CBOR parse fails (serde_cbor::from_slice)
  • error is mapped to structured ABI error envelope (success=false, error_code=INVALID_CBOR)

Building Ownables

Build command:

cargo build --target wasm32-unknown-unknown --release

This produces a wasm module consumable by a host/runtime that implements Host ABI v1.

Contract-side requirements:

  • do not export runtime entrypoints with #[wasm_bindgen]
  • do not rely on generated *.js glue for host calls
  • export only the stable ownable_* ABI symbols (via ownable_host_abi_v1!)

End-to-End Test Vector

Example contract handler:

fn instantiate_handler(input: &[u8]) -> Result<Vec<u8>, ownable_std::abi::HostAbiError> {
    Ok(input.to_vec())
}

Host request bytes (CBOR, decoded form shown):

{ ping: "pong" }

Expected output envelope bytes decode to the same payload bytes.

{
  success: true,
  payload: <CBOR bytes for { ping: "pong" }>
}

Other Utilities in This Crate

  • MemoryStorage: in-memory storage implementation for testing/off-chain execution
  • create_env / create_ownable_env: env builders
  • package_title_from_name, color helpers, metadata/shared message structs
  • ownable-std-macros: attribute macros to extend execute/query/instantiate messages

Versioning Guidance

  • set your package manifest field ownablesAbi to "1" for exported symbol/signature ABI compatibility
  • set your package manifest field wireFormat to "cbor" for payload envelope encoding compatibility
  • host/runtime should reject incompatible versions
  • non-interface dependency changes should not require runtime changes if ABI version stays the same