npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@donezone/client

v0.1.55

Published

Client library for interacting with Done contracts from JavaScript/TypeScript (browser, Node, Bun). Handles envelope construction, signing, transport to the Done HTTP gateway, and SSE event subscriptions.

Readme

@donezone/client

Client library for interacting with Done contracts from JavaScript/TypeScript (browser, Node, Bun). Handles envelope construction, signing, transport to the Done HTTP gateway, and SSE event subscriptions.


Table of Contents

  1. Concepts
  2. Installation
  3. The Done Facade
  4. DoneContractClient
  5. DoneBackendClient
  6. Envelope Builders
  7. Low-Level Primitives
  8. Message Builders
  9. Types Reference

Concepts

Every write operation on a Done contract requires an auth envelope — a signed payload that wraps one or more CosmWasm messages with identity, nonce, expiry, and signatures. This package handles building those envelopes, signing them (via pluggable signer callbacks), and submitting them to POST /tx on the Done HTTP gateway. Read operations are plain GET /query/{contract}?msg=... calls with no signing required.

Call flow for a transaction:

Done.run(url, opts)
  → buildTransactionMessage   (produces WasmMsg::Execute)
  → EnvelopeBuilder callback  (user provides signing logic)
  → backend.executeEnvelope   (POST /tx)

Call flow for a query:

Done.query(url, opts)
  → buildQueryMessage         (serialises path/body/query)
  → GET /query/{contract}?msg=...

Call flow for events:

Done.subscribe(eventsUrl, topic, handler)
  → EventSource SSE connection
  → JSON-parsed events forwarded to handler

Installation

bun add @donezone/client
# or
npm install @donezone/client

The Done Facade

Done is a pre-configured singleton exported from the package. Most applications only need this.

import { Done, signDocDigest } from "@donezone/client";

Done.config(options)

Mutates the global Done instance. Call once at app startup. Omitted fields keep their existing values.

Done.config({
  doneHttp: "https://doneHttp.done.zone",    // Done HTTP gateway base URL
  doneEvents: "https://doneEvents.done.zone", // Done Events SSE base URL
  signer: myEnvelopeBuilder,                  // EnvelopeBuilder (see Envelope Builders)
  fetch: customFetch,                         // optional custom fetch implementation
  eventSource: customEventSourceFactory,      // optional custom EventSource factory
});

Done.run(url, options?)

Executes an authenticated transaction. Returns Promise<ExecuteResponse>.

url is resolved against doneHttp. The first path segment after the base must be the contract address, followed by the route path:

const result = await Done.run("/done1contract.../buy", {
  body: { minAmountOut: "1" },
  funds: { uusd: "100" },      // or [{ denom: "uusd", amount: "100" }]
  memo: "optional memo",
  gasLimit: 500_000,
  traceId: "abc-123",
  signal: abortController.signal,
});
// result: { transactionHash, height, gasUsed }

Requires signer to be configured on the Done instance.

Done.query<T>(url, options?)

Issues a read-only query. Returns Promise<T>.

const price = await Done.query<{ amount: string }>("/done1contract.../price");

const orders = await Done.query("/done1contract.../orders", {
  search: { limit: 10, offset: 0 }, // merged into querystring
});

Done.subscribe(eventsUrl, topic, handler, options?)

Opens an SSE stream. Returns an () => void cleanup function.

eventsUrl is resolved against doneEvents. The path must include the contract address (e.g. /done1contract.../events or just the address segment the events server expects).

const stop = Done.subscribe(
  "/done1contract.../events",
  "PriceChange",             // topic string; also accepts string[] for nested paths
  (event) => {
    console.log(event);      // parsed JSON; raw string if parsing fails
  },
  {
    rawEvent: true,          // forward the raw MessageEvent instead of parsing
    signal: ac.signal,       // AbortSignal closes the subscription automatically
    onError: (e) => {},      // called on EventSource error events
  },
);

// later:
stop();

Done.create(options?)

Returns a new isolated DoneInstance with its own settings, inheriting from the current Done configuration. Useful for multi-tenant dashboards or per-request contexts.

const staging = Done.create({
  doneHttp: "https://doneHttp.staging.zone",
  doneEvents: "https://doneEvents.staging.zone",
  signer: stagingSigner,
});

await staging.run("/done1contract.../buy", { body: { minAmountOut: "5" } });

The returned instance has the same run, query, subscribe, config, create, and contract methods as the global Done.

Done.contract(address) / Done.contract(config)

Returns a DoneContractClient (or DoneContractHandle) scoped to a specific contract. The instance inherits signer, fetch, and eventSource from the Done configuration.

// Simple: address string — inherits everything from Done config
const sale = Done.contract("done1contract123");
await sale.run("/buy", { body: { minAmountOut: "1" } });
const price = await sale.query<{ amount: string }>("/price");

// Advanced: full config object
const sale = Done.contract({
  baseUrl: "https://doneHttp.done.zone",
  address: "done1contract123",
  buildEnvelope: customSigner,
  eventsPath: "/events",
});

Done.buildEnvelope(draft) / Done.signDocDigest(envelope) / Done.toSignDoc(envelope)

Convenience re-exports of the low-level primitives (see Low-Level Primitives).


DoneContractClient

A strongly typed, contract-scoped client. Instantiate directly when you need per-contract configuration (e.g. a different signer or events URL).

import { DoneContractClient } from "@donezone/client";

const client = new DoneContractClient({
  baseUrl: "https://doneHttp.done.zone",
  address: "done1contract123",
  buildEnvelope: myEnvelopeBuilder, // required to call execute/run/post
  eventsPath: "/events",            // default: "/events"
  eventSource: myEventSourceFactory,
  fetch: customFetch,
  defaultMetadata: { gas_limit: 500_000 },
});

Type Safety for Transactions

execute, run, and post require buildEnvelope to be present in the config. If it is absent, TypeScript will give a compile-time error (Argument of type 'string' is not assignable to parameter of type 'never') rather than a runtime throw. Clients without buildEnvelope are limited to query, get, subscribe, and transaction.

Methods

client.query<T>(path, request?, init?)

Read-only query. Returns Promise<T>.

const state = await client.query<{ count: number }>("/state");
const orders = await client.query("/orders", { query: { limit: "10" } });

client.get(path, request?, init?)

Same as query but returns a DoneRequest (a PromiseLike<Response> with convenience .json<T>(), .text(), .arrayBuffer() methods). Useful when you need the raw Response.

const res = await client.get("/orders", { query: { limit: "10" } });
const data = await res.json<Order[]>();

client.execute(path, request?, init?) / .run(...) / .post(...)

Execute a transaction. All three are identical. Requires buildEnvelope in config (compile-time enforced). Returns Promise<ExecuteResponse>.

const result = await client.execute("/buy", {
  body: { minAmountOut: "1" },
  funds: { uusd: "100" },
  memo: "purchase",
  gasLimit: 300_000,
  traceId: "trace-xyz",
  signal: ac.signal,
});
// result: { transactionHash, height, gasUsed }

client.transaction(path, request?)

Builds a PreparedTransaction (the CosmosMsg + optional metadata) without submitting it. Use this to batch multiple contract calls into a single envelope manually.

const tx = client.transaction("/buy", { body: { minAmountOut: "1" } });
// tx: { msg: CosmosMsg, metadata?: EnvelopeMetadata }

client.subscribe(topic, handler, options?)

Opens an SSE subscription to {baseUrl}{eventsPath}/{address}?topic={topic}. Returns () => void.

const stop = client.subscribe("Pinged", (payload) => {
  console.log(payload); // parsed JSON
});

// With options:
const stop = client.subscribe(
  ["namespace", "event"],  // string[] encodes as JSON topic
  handler,
  { onError: console.error, signal: ac.signal },
);

client.publishCode(request, init?)

Publishes or updates contract code. Returns Promise<PublishCodeResponse>.

await client.publishCode({ script: contractSourceCode, msg: initMsg });

client.buildEnvelope(draft) / client.signDocDigest(envelope)

See Low-Level Primitives.


DoneBackendClient

The raw transport layer. Use this when you need full control over envelope construction and submission.

import { DoneBackendClient } from "@donezone/client";

const backend = new DoneBackendClient({
  baseUrl: "https://doneHttp.done.zone",
  fetch: customFetch, // optional
});

Methods

backend.contract(address)

Returns a DoneContractHandle with transaction, query, get, and publishCode helpers. Does not include execute/run — use backend.executeEnvelope for submission.

const handle = backend.contract("done1contract123");
const tx = handle.transaction("/buy", { body: { minAmountOut: "1" } });
// tx: PreparedTransaction — pass tx.msg into buildEnvelope

backend.executeEnvelope(envelope, options?, init?)

Submit a fully-built AuthEnvelope to POST /tx. Returns Promise<ExecuteResponse>.

await backend.executeEnvelope(envelope, {
  passkey: { publicKey: "base64-encoded-public-key" },
  memo: "tx memo",
  signal: ac.signal,
});

backend.queryContract<T>(address, call, init?)

const result = await backend.queryContract<{ price: string }>(
  "done1contract123",
  { path: "/price" },
);

backend.queryContractRaw(address, call, init?)

Same as queryContract but returns the raw Response.

backend.publishCode(request, init?)

await backend.publishCode({
  contract: "js",          // or "wasm"
  script: contractSource,
  msg: optionalInitMsg,
});

backend.getAgentQuota(userId, agent) / backend.getForwarderQuota(address)

Inspect quota usage for agent/forwarder authorization.


Envelope Builders

Envelope builders are functions with the signature (ctx: EnvelopeBuildContext) => Promise<EnvelopeBuildResult> | EnvelopeBuildResult. Pass them as signer to Done.config() or buildEnvelope to DoneContractClient.

createPasskeyEnvelopeBuilder(options)

For WebAuthn / hardware passkey signing.

import { createPasskeyEnvelopeBuilder, signDocDigest } from "@donezone/client";

const builder = createPasskeyEnvelopeBuilder({
  userId: "user-abc",                          // or (ctx) => resolveUserId(ctx)
  nonce: () => Date.now(),
  expiresAt: () => Math.floor(Date.now() / 1000) + 300,
  publicKey: () => storedPublicKey,            // base64 string
  sign: async (signDoc, ctx) => {
    const digest = signDocDigest(signDoc);     // SHA-256 Uint8Array
    return await signWithHardware(digest);     // return Uint8Array or base64 string
  },
  // Optional:
  agent: "done1agent...",
  forwarder: "done1forwarder...",
  metadata: { gas_limit: 500_000 },
});

Done.config({ signer: builder });

All option fields except sign can be a plain value or a (ctx: EnvelopeBuildContext) => MaybePromise<T> factory, giving per-call control.

createSessionEnvelopeBuilder(options)

For session key signing (e.g. a server-side HSM or stored keypair).

import { createSessionEnvelopeBuilder } from "@donezone/client";

const builder = createSessionEnvelopeBuilder({
  userId: "user-abc",
  sessionId: "session-xyz",
  nonce: () => Date.now(),
  expiresAt: () => Math.floor(Date.now() / 1000) + 300,
  sign: async (signDoc) => {
    const digest = signDocDigest(signDoc);
    return await signWithSessionKey(digest); // Uint8Array or base64
  },
});

createDevEnvelopeBuilder(options?)

Produces unsigned envelopes for local development and testing. Never use in production.

import { createDevEnvelopeBuilder } from "@donezone/client";

const builder = createDevEnvelopeBuilder({
  userId: "dev",          // default: "dev"
  ttlSeconds: 300,        // default: 300
});

Low-Level Primitives

These are useful when implementing a custom envelope builder or doing manual envelope manipulation.

buildEnvelope(draft)

Constructs and validates an AuthEnvelope from a draft. Throws if msgs is empty.

import { buildEnvelope } from "@donezone/client";

const envelope = buildEnvelope({
  user_id: "user-abc",
  msgs: [cosmosMsg],
  nonce: Date.now(),
  expires_at: Math.floor(Date.now() / 1000) + 300,
  role: "Passkey",          // or "Session"
  signatures: { passkey: "base64sig" },
  // Optional:
  session_id: "...",
  agent: "done1agent...",
  forwarder: "done1forwarder...",
  metadata: { trace_id: "...", memo: "...", gas_limit: 500_000 },
});

toSignDoc(envelope)

Strips signatures from an envelope (or draft) to produce the canonical AuthEnvelopeSignDoc that is actually signed.

import { toSignDoc } from "@donezone/client";

const signDoc = toSignDoc(envelope);

signDocBytes(signDoc)

Produces the canonical UTF-8 JSON bytes (keys sorted, no extra whitespace) of the sign doc. This is what gets hashed.

import { signDocBytes } from "@donezone/client";

const bytes = signDocBytes(signDoc); // Uint8Array

signDocDigest(signDoc)

SHA-256 of the canonical sign doc bytes. This is what passkey/session hardware actually signs.

import { signDocDigest } from "@donezone/client";

const digest = signDocDigest(signDoc); // Uint8Array (32 bytes)

encodeSignature(bytes) / decodeSignature(base64)

Convert between raw signature bytes and the base64 strings stored in AuthEnvelope.signatures.

import { encodeSignature, decodeSignature } from "@donezone/client";

const b64 = encodeSignature(rawSigBytes);  // Uint8Array → base64 string
const raw = decodeSignature(b64);          // base64 string → Uint8Array

Message Builders

These build the wire format for contract calls. Useful when constructing multi-message envelopes manually.

buildTransactionMessage(contractAddr, call, options?)

Builds a WasmMsg::Execute CosmosMsg in the shape the Done HTTP gateway expects.

import { buildTransactionMessage } from "@donezone/client";

const msg = buildTransactionMessage(
  "done1contract123",
  { path: "/buy", body: { minAmountOut: "1" }, query: { mode: "fast" } },
  { funds: { uusd: "100" } },
);
// { wasm: { execute: { contract_addr, msg: { msg: { path, body, query } }, funds } } }

The call.path must start with /.

buildQueryMessage(call)

Builds the query message object that goes into GET /query/{contract}?msg=....

import { buildQueryMessage } from "@donezone/client";

const queryMsg = buildQueryMessage({ path: "/price", query: { verbose: "1" } });
// { msg: { path: "/price", query: { verbose: "1" } } }

Types Reference

Key types exported from the package:

// Envelope core
AuthEnvelope         // complete signed envelope sent to /tx
AuthEnvelopeSignDoc  // envelope minus signatures — the thing that gets signed
EnvelopeDraft        // input to buildEnvelope (signatures optional)
EnvelopeRole         // "Passkey" | "Session"
EnvelopeSignatures   // { passkey?: string; session?: string }
EnvelopeMetadata     // { trace_id?: string; memo?: string; gas_limit?: number }

// Messages
CosmosMsg            // WasmExecuteMsg | BankSendMsg | Record<string, unknown>
WasmExecuteMsg       // { wasm: { execute: { contract_addr, msg, funds? } } }
BankSendMsg          // { bank: { send: { to_address, amount } } }
Coin                 // { denom: string; amount: string }
FundsInput           // Coin[] | Record<string, string | number> | undefined

// Client config
DoneContractConfig   // config for DoneContractClient constructor
DoneClientConfig     // config for DoneBackendClient constructor
DoneConfigOptions    // config for Done.config() / Done.create()

// Request/response
ExecuteResponse      // { transactionHash: string; height: number; gasUsed: number }
TransactionRequest   // options for execute/run/post (body, query, funds, memo, etc.)
QueryRequest         // options for query/get (body, query)
PreparedTransaction  // { msg: CosmosMsg; metadata?: EnvelopeMetadata }
SubscriptionOptions  // { onError?, signal?, rawEvent? }

// Builders
EnvelopeBuilder      // (ctx: EnvelopeBuildContext) => Promise<EnvelopeBuildResult> | EnvelopeBuildResult
EnvelopeBuildContext // { msg, metadata?, path, request }
EnvelopeBuildResult  // { envelope: AuthEnvelope; options?: ExecuteEnvelopeOptions }
EventSourceFactory   // (url: string) => EventSource-compatible object

// Passkey/session builder options
PasskeyEnvelopeBuilderOptions
SessionEnvelopeBuilderOptions
DevEnvelopeBuilderOptions

Complete Example

import {
  Done,
  createPasskeyEnvelopeBuilder,
  signDocDigest,
} from "@donezone/client";

// Configure once at startup
Done.config({
  doneHttp: "https://doneHttp.done.zone",
  doneEvents: "https://doneEvents.done.zone",
  signer: createPasskeyEnvelopeBuilder({
    userId: () => currentUser.id,
    nonce: () => Date.now(),
    expiresAt: () => Math.floor(Date.now() / 1000) + 300,
    publicKey: () => currentUser.passkeyPublicKey,
    sign: async (signDoc) => {
      const digest = signDocDigest(signDoc);
      return await navigator.credentials.get(/* WebAuthn options using digest */);
    },
  }),
});

// Transaction
const { transactionHash } = await Done.run("/done1abc.../buy", {
  body: { minAmountOut: "100" },
  funds: { uusd: "1000" },
});

// Query
const price = await Done.query<{ amount: string }>("/done1abc.../price");

// Subscription
const stop = Done.subscribe(
  "/done1abc.../events",
  "PriceChange",
  (event) => console.log("price changed:", event),
);