@inversealtruism/cairn-sdk 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -39,8 +39,8 @@ cairn.index.streamAll({ onBlock: (e) => console.log("new block", e) });
39
39
 
40
40
  The SDK follows the exact model MetaMask uses, there is **no allowlist you maintain**:
41
41
 
42
- - **`connect()` / `getAddress()`** grant *address visibility*. The first time a site connects, the user approves once; after that the wallet answers silently (the site shows up under **Settings → Connected sites**, revocable anytime). This is the only "silent" call.
43
- - **Every signing / fund-moving call:** `send`, `propose`, `attest`, `signIn`, `sealClaim`, `revealClaim`, **always** opens the wallet's clear-signing approval window, **every time**, no matter what. Being "connected" never pre-approves a signature. A connected site cannot move a satoshi without an explicit, fully-disclosed approval click.
42
+ - **`connect()` / `getAddress()`** grant *address visibility*. The first time a site connects, the user approves once; after that the wallet answers silently (the site shows up under **Settings → Connected sites**, revocable anytime). These are the only calls that can return the address without a prompt. `getPermissions()` (read this site's own grant) and `revokePermissions()` (drop it) are silent too; neither signs anything.
43
+ - **Every signing / fund-moving call:** `send`, `propose`, `attest`, `fillOffer`, `sealClaim`, `revealClaim` and `signInWithCsd`, **always** opens the wallet's clear-signing approval window, **every time**, no matter what. Being "connected" never pre-approves a signature. A connected site cannot move a satoshi without an explicit, fully-disclosed approval click. The legacy `signIn()` is deprecated and first-party-only: the wallet refuses it from any other site with `FIRST_PARTY_ONLY` before prompting; use `signInWithCsd()`.
44
44
 
45
45
  ```ts
46
46
  import { connect, UserRejectedError, NotInstalledError } from "@inversealtruism/cairn-sdk";
@@ -58,7 +58,7 @@ try {
58
58
 
59
59
  **`cairn.index`:** `tipHeight()`, `block()/blockTxids()`, `tx()/txStatus()/txMerkleProof()`, `address()/addressTxs()/addressUtxo()`, `domains()/domainProposals(domain)`, `proposal()/attestations()`, `reputation()`, **`verifyInclusion(txid)`** (trust-minimized), `streamAll()/streamBlocks()/streamDomain()` (SSE), `subscribe()` (WS).
60
60
 
61
- **`cairn.names`:** `resolve(name)` (display-path name to address), `name(name)`/`nameHistory(name)`, `primary(addr)` (reverse lookup)/`namesOf(addr)`, `names()`, `tokens()/token(ticker)`, `offers()`, `bids()`, `state()` (the canonical CairnX state). Reads go through the public `/trade/api/cairnx/*` proxy; for payment-grade name resolution verify through the wallet or a replaying resolver, not a bare read (see `src/names.ts` header).
61
+ **`cairn.names`:** `resolve(name)` (display-path name to address), `name(name)`/`nameHistory(name)`, `primary(addr)` (reverse lookup)/`namesOf(addr)`, `names()`, `tokens()/token(ticker)`, `offers()`/`bids()` (open rows by default; pass `{ status }` for another status, or `{ status: "all" }` for every row), `state()` (the canonical CairnX state). Reads go through the public `/trade/api/cairnx/*` proxy; for payment-grade name resolution verify through the wallet or a replaying resolver, not a bare read (see `src/names.ts` header).
62
62
 
63
63
  **`cairn.content`:** `prepare(obj)`/`put(obj)` → `{ payloadHash, canonical, bytes }`; `get(hash)` / `getBytes(hash)` (fetch **and verify** `sha256(bytes) === hash`, throws `ContentVerificationError` on tamper); `hash(obj)`.
64
64
 
@@ -68,14 +68,13 @@ Subpath imports are available for tree-shaking: `@inversealtruism/cairn-sdk/{con
68
68
 
69
69
  ## Examples
70
70
 
71
- - **`examples/read-only.mjs`:** runs today against live mainnet, no wallet, no build: `npm run test:live`. Demonstrates chain/indexer reads + verified merkle inclusion + verified content fetch.
72
- - **`examples/hello-csd/`:** a no-framework browser dApp (connect → read recent proposals → live feed). Build the bundle with `npm run build:example`, then serve the folder from **localhost** (the wallet injects there) with the Cairn Wallet installed.
71
+ - **`examples/read-only.mjs`:** runs against live mainnet with no wallet. It imports the built `dist/`, so run `pnpm build` first, then `pnpm test:live`. Demonstrates chain/indexer reads + verified merkle inclusion + verified content fetch.
72
+ - **`examples/hello-csd/`:** a no-framework browser dApp (connect → read recent proposals → live feed). Build the bundle with `pnpm build:example`, then serve the folder from **localhost** (the wallet injects there) with the Cairn Wallet installed.
73
73
 
74
74
  ## Trust model & honest limits
75
75
 
76
- - **Content is always verified client-side.** `cairn.content.get()` rejects bytes that don't hash to the requested `payloadHash`, an untrusted gateway can't serve tampered content.
77
- - **Merkle inclusion is trust-minimized, to the strength of your header source.** `verifyInclusion()` folds the proof and cross-checks its root against a header merkle from `headerMerkleAt`. The default `Cairn` wiring backs that with a light client that PoW-verifies headers forward from a pinned checkpoint, so a match reads `trustLevel: "verified-inclusion"` with `included: true`. A header within 12 blocks of the light client's cached tip is first compared with the header the base serves at that height, so a block orphaned by a reorg stops counting; the hosted base edge-caches rows at or below its tip minus 6, so there this catches reorgs of up to 6 blocks. When no header can be verified (below the checkpoint, header source unreachable, no source configured) the result is `"proof-consistent"` with `included: false` and a `reason`: the indexer alone vouches for it. Gate on `included` or on `trustLevel === "verified-inclusion"`; they agree.
78
- - **Balances are RPC-trusted.** Headers don't commit to the UTXO set; balance reads trust the node/proxy (same as every light client).
76
+ Content bytes are always verified client-side; merkle inclusion is verified when a PoW-verified header backs it (`trustLevel: "verified-inclusion"`); balances are RPC-trusted. The full trust model (each `trustLevel`, the checkpoint, reorgs near the tip) is in [`docs/SDK-GUIDE.md`](docs/SDK-GUIDE.md#what-the-sdk-verifies-vs-trusts-read-this).
77
+
79
78
  - **Content comes from the cairn origin.** `cairn.content` reads `/content/<hash>` on the cairn base and verifies every byte against the hash.
80
79
  - **The indexer runs behind the cairn proxy.** By default `cairn.index` uses the cairn server's hardened, read-only, rate-limited `/explorer/api` reverse proxy (no separate public indexer needed). **SSE works through that proxy; `subscribe()` (WebSocket) needs a direct indexer endpoint:** set `baseUrls.indexer` to a private/localhost indexer for WS.
81
80
  - **The wallet injects on every site** (connect-anywhere shipped in 0.2.4x) behind a per-origin consent gate: a site sees nothing until the user approves the connection, and some request kinds stay first-party-only (the `FIRST_PARTY_ONLY` error code).
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  CairnError,
3
3
  ContentVerificationError
4
- } from "./chunk-PBKDSDID.js";
4
+ } from "./chunk-UAPWZZQK.js";
5
5
 
6
6
  // src/content.ts
7
7
  import { payloadHash, canonicalJson, verifyContentBytes } from "@inversealtruism/csd-codec";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getWallet
3
- } from "./chunk-4FFLCCL4.js";
3
+ } from "./chunk-WWO3FXHG.js";
4
4
 
5
5
  // src/controller.ts
6
6
  var DISCONNECTED = Object.freeze({ status: "disconnected", account: null, error: null });
@@ -1,5 +1,5 @@
1
1
  // src/errors.ts
2
- var SDK_VERSION = "0.7.0";
2
+ var SDK_VERSION = "0.8.0";
3
3
  var DOCS_BASE = "https://cairn-substrate.com/docs/sdk";
4
4
  var CairnError = class extends Error {
5
5
  /** Stable machine code for programmatic branching. */
@@ -4,7 +4,7 @@ import {
4
4
  UnsupportedMethodError,
5
5
  mapProviderError,
6
6
  mapSubmitResultError
7
- } from "./chunk-PBKDSDID.js";
7
+ } from "./chunk-UAPWZZQK.js";
8
8
 
9
9
  // src/connect.ts
10
10
  var DEFAULT_PROVIDER_TIMEOUT_MS = 10 * 6e4;
package/dist/connect.cjs CHANGED
@@ -30,7 +30,7 @@ __export(connect_exports, {
30
30
  module.exports = __toCommonJS(connect_exports);
31
31
 
32
32
  // src/errors.ts
33
- var SDK_VERSION = "0.7.0";
33
+ var SDK_VERSION = "0.8.0";
34
34
  var DOCS_BASE = "https://cairn-substrate.com/docs/sdk";
35
35
  var CairnError = class extends Error {
36
36
  /** Stable machine code for programmatic branching. */
package/dist/connect.js CHANGED
@@ -5,8 +5,8 @@ import {
5
5
  discoverProviders,
6
6
  getWallet,
7
7
  isInstalled
8
- } from "./chunk-4FFLCCL4.js";
9
- import "./chunk-PBKDSDID.js";
8
+ } from "./chunk-WWO3FXHG.js";
9
+ import "./chunk-UAPWZZQK.js";
10
10
  export {
11
11
  WalletConnection,
12
12
  connect,
package/dist/content.cjs CHANGED
@@ -26,7 +26,7 @@ module.exports = __toCommonJS(content_exports);
26
26
  var import_csd_codec = require("@inversealtruism/csd-codec");
27
27
 
28
28
  // src/errors.ts
29
- var SDK_VERSION = "0.7.0";
29
+ var SDK_VERSION = "0.8.0";
30
30
  var DOCS_BASE = "https://cairn-substrate.com/docs/sdk";
31
31
  var CairnError = class extends Error {
32
32
  /** Stable machine code for programmatic branching. */
package/dist/content.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ContentClient
3
- } from "./chunk-TDXGYP3G.js";
4
- import "./chunk-PBKDSDID.js";
3
+ } from "./chunk-DTPADO7N.js";
4
+ import "./chunk-UAPWZZQK.js";
5
5
  export {
6
6
  ContentClient
7
7
  };
@@ -25,7 +25,7 @@ __export(controller_exports, {
25
25
  module.exports = __toCommonJS(controller_exports);
26
26
 
27
27
  // src/errors.ts
28
- var SDK_VERSION = "0.7.0";
28
+ var SDK_VERSION = "0.8.0";
29
29
  var DOCS_BASE = "https://cairn-substrate.com/docs/sdk";
30
30
  var CairnError = class extends Error {
31
31
  /** Stable machine code for programmatic branching. */
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  CairnController
3
- } from "./chunk-XOGOSGC6.js";
4
- import "./chunk-4FFLCCL4.js";
5
- import "./chunk-PBKDSDID.js";
3
+ } from "./chunk-JXBO4LV6.js";
4
+ import "./chunk-WWO3FXHG.js";
5
+ import "./chunk-UAPWZZQK.js";
6
6
  export {
7
7
  CairnController
8
8
  };
package/dist/index.cjs CHANGED
@@ -58,7 +58,7 @@ __export(index_exports, {
58
58
  module.exports = __toCommonJS(index_exports);
59
59
 
60
60
  // src/errors.ts
61
- var SDK_VERSION = "0.7.0";
61
+ var SDK_VERSION = "0.8.0";
62
62
  var DOCS_BASE = "https://cairn-substrate.com/docs/sdk";
63
63
  var CairnError = class extends Error {
64
64
  /** Stable machine code for programmatic branching. */
@@ -765,7 +765,8 @@ function normalizeHash(hash) {
765
765
  // src/names.ts
766
766
  var import_cairnx_core = require("@inversealtruism/cairnx-core");
767
767
  var nameOk = (n) => typeof n === "string" && import_cairnx_core.NAME_RE.test(n) && !import_cairnx_core.RESERVED_NAMES.has(n);
768
- var amountOk = (v) => typeof v === "string" && import_cairnx_core.AMOUNT_RE.test(v);
768
+ var amountOk = (v) => (0, import_cairnx_core.parseAmount)(v, { allowZero: true }) !== null;
769
+ var decimalsOk = (d) => Number.isSafeInteger(d) && d >= 0 && d <= import_cairnx_core.MAX_TOKEN_DECIMALS;
769
770
  var heightOk = (v) => Number.isSafeInteger(v) && v >= 0;
770
771
  function nameHistoryProblem(h, name) {
771
772
  if (!h || typeof h !== "object" || Array.isArray(h)) return "not an object";
@@ -782,14 +783,15 @@ function nameHistoryProblem(h, name) {
782
783
  }
783
784
  return null;
784
785
  }
785
- function tokenProblem(t) {
786
+ function tokenProblem(t, ticker) {
786
787
  if (!t || typeof t !== "object") return "not an object";
787
788
  const r = t;
788
789
  if (typeof r.ticker !== "string" || !import_cairnx_core.TICKER_RE.test(r.ticker)) return "bad ticker";
789
- if (!Number.isSafeInteger(r.decimals) || r.decimals < 0) return `bad decimals (${String(r.decimals)})`;
790
- if (!amountOk(r.supply)) return "supply is not a base-unit integer string";
791
- if (!amountOk(r.minted)) return "minted is not a base-unit integer string";
792
- if (r.mintLimit !== void 0 && !amountOk(r.mintLimit)) return "mintLimit is not a base-unit integer string";
790
+ if (ticker !== void 0 && r.ticker !== ticker) return `ticker is ${JSON.stringify(r.ticker)}, expected ${JSON.stringify(ticker)}`;
791
+ if (!decimalsOk(r.decimals)) return `bad decimals (${String(r.decimals)}), not an integer in the consensus range`;
792
+ if (!amountOk(r.supply)) return "supply is not a base-unit integer string within MAX_AMOUNT";
793
+ if (!amountOk(r.minted)) return "minted is not a base-unit integer string within MAX_AMOUNT";
794
+ if (r.mintLimit !== void 0 && !amountOk(r.mintLimit)) return "mintLimit is not a base-unit integer string within MAX_AMOUNT";
793
795
  return null;
794
796
  }
795
797
  var NamesClient = class {
@@ -800,11 +802,17 @@ var NamesClient = class {
800
802
  malformed(path, what) {
801
803
  return new HttpError(200, this.http.url(path), `malformed response: ${what}`);
802
804
  }
803
- async list(path) {
804
- const body = await this.http.getJson(path);
805
+ async list(path, query) {
806
+ const body = await this.http.getJson(path, query);
805
807
  if (!Array.isArray(body)) throw this.malformed(path, "expected an array");
806
808
  return body;
807
809
  }
810
+ /** Rows of a market list with one status, or every row for "all". */
811
+ async byStatus(path, status) {
812
+ if (status === "all") return this.list(path);
813
+ const rows = await this.list(path, { status });
814
+ return rows.filter((r) => r?.status === status);
815
+ }
808
816
  /** Resolve a .csd name for DISPLAY (server-trusted; see the module trust note). null = unknown/invalid name. */
809
817
  async resolve(name) {
810
818
  if (!nameOk(name)) return null;
@@ -875,7 +883,8 @@ var NamesClient = class {
875
883
  });
876
884
  return rows;
877
885
  }
878
- /** One token by ticker. null = unknown/invalid ticker. Throws a typed HttpError on a malformed body. */
886
+ /** One token by ticker. null = unknown/invalid ticker. Throws a typed HttpError on a malformed body or a
887
+ * record for another ticker. */
879
888
  async token(ticker) {
880
889
  if (typeof ticker !== "string" || !import_cairnx_core.TICKER_RE.test(ticker)) return null;
881
890
  const path = `/trade/api/cairnx/token/${ticker}`;
@@ -886,17 +895,19 @@ var NamesClient = class {
886
895
  if (e?.status === 404) return null;
887
896
  throw e;
888
897
  }
889
- const p = tokenProblem(t);
898
+ const p = tokenProblem(t, ticker);
890
899
  if (p) throw this.malformed(path, p);
891
900
  return t;
892
901
  }
893
- /** Open offers (the market's display list). */
894
- offers() {
895
- return this.list(`/trade/api/cairnx/offers`);
902
+ /** Offers with one status, open ones by default (the market's display list). `{ status: "all" }` returns every
903
+ * offer the server holds, filled, cancelled and expired ones included. */
904
+ offers(opts = {}) {
905
+ return this.byStatus(`/trade/api/cairnx/offers`, opts.status ?? "open");
896
906
  }
897
- /** Open bids. */
898
- bids() {
899
- return this.list(`/trade/api/cairnx/bids`);
907
+ /** Bids with one status, open ones by default. `{ status: "all" }` returns every bid the server holds, done,
908
+ * cancelled and expired ones included. */
909
+ bids(opts = {}) {
910
+ return this.byStatus(`/trade/api/cairnx/bids`, opts.status ?? "open");
900
911
  }
901
912
  /** The full canonical CairnX state (large; prefer the scoped reads above). */
902
913
  state() {
@@ -1309,10 +1320,10 @@ async function preverifyOffer(opts) {
1309
1320
  return { ok: false, trust: "verified", reason: "the served offer is missing seller (the rebate recipient) - pass the resolver's full offer object, or omit servedOffer", ...base };
1310
1321
  if (String(so.seller).toLowerCase() !== seller)
1311
1322
  return { ok: false, trust: "verified", reason: "the served seller doesn't match the offer's on-chain author (a lying resolver may be redirecting your payment)", ...base };
1312
- if ((0, import_cairnx_core2.bindOfferTerms)(opts.servedOffer, terms, { give: true, wantType: true }))
1313
- return { ok: false, trust: "verified", reason: "the served terms don't match the offer's on-chain record (fee, rebate, partial sizing, the give, or the want type - a lying resolver could mis-size or re-route the fill, redirecting or burning your payment)", ...base };
1314
1323
  if ((0, import_cairnx_core2.isTokenWant)(offerRec.want) && (String(so?.want?.ticker) !== String(offerRec.want.ticker) || String(so?.want?.amount) !== String(offerRec.want.amount)))
1315
1324
  return { ok: false, trust: "verified", reason: "the served want ticker/amount don't match the offer's on-chain record (a lying resolver could bait-and-switch the token price)", ...base };
1325
+ if ((0, import_cairnx_core2.bindOfferTerms)(opts.servedOffer, terms, { give: true, wantType: true }))
1326
+ return { ok: false, trust: "verified", reason: "the served terms don't match the offer's on-chain record (fee, rebate, partial sizing, the give, or the want type - a lying resolver could mis-size or re-route the fill, redirecting or burning your payment)", ...base };
1316
1327
  }
1317
1328
  if (sumsMismatch) return { ok: false, trust: "verified", reason: sumsMismatch, ...base };
1318
1329
  return { ok: true, trust: "verified", ...base };
package/dist/index.d.cts CHANGED
@@ -5,7 +5,7 @@ import { IndexerClient } from './indexer.cjs';
5
5
  export { InclusionResult, IndexEvent, MerkleProof, StreamHandle, StreamHandlers, TrustLevel } from './indexer.cjs';
6
6
  import { ContentClient } from './content.cjs';
7
7
  export { ContentClientOptions, PreparedContent } from './content.cjs';
8
- import { NameState, MintedProvenOfferTerms } from '@inversealtruism/cairnx-core';
8
+ import { NameState, OfferStatus, BidStatus, MintedProvenOfferTerms } from '@inversealtruism/cairnx-core';
9
9
  export { FillEndorsement, FillOutputPlan, FillPreview, FillSafety, MintedProvenOfferTerms, ProvenOfferTerms, bindOfferTerms, feeBpsAt, fillEndorsement, fillIsSafe, fillOutputPlan, previewFill, requiredFillOutputs } from '@inversealtruism/cairnx-core';
10
10
  import { WalletConnection, DetectOptions } from './connect.cjs';
11
11
  export { AttestParams, CairnProvider, ConnectResult, FillParams, ProposeParams, ProviderDetail, ProviderInfo, SealClaimParams, SendParams, SignInResult, SiwcParams, SiwcResult, TxResult, WalletCapabilities, WalletPermission, connect, detectProvider, discoverProviders, getWallet, isInstalled } from './connect.cjs';
@@ -16,7 +16,7 @@ import '@inversealtruism/csd-tx';
16
16
  import '@inversealtruism/csd-codec';
17
17
  import '@inversealtruism/csd-crypto';
18
18
 
19
- /** `/cairnx/resolve/:name` — the display-path name→address resolution. */
19
+ /** `/cairnx/resolve/:name`: the display-path name→address resolution. */
20
20
  interface NameResolution {
21
21
  ok: boolean;
22
22
  name: string;
@@ -28,7 +28,7 @@ interface NameResolution {
28
28
  owner?: string;
29
29
  tipHeight?: number;
30
30
  }
31
- /** `/cairnx/name/:name` — full name detail (lease, lock, resting offer, recapture window). */
31
+ /** `/cairnx/name/:name`: full name detail (lease, lock, resting offer, recapture window). */
32
32
  interface NameDetail {
33
33
  name: string;
34
34
  owner: string;
@@ -101,6 +101,8 @@ declare class NamesClient {
101
101
  constructor(http: Http);
102
102
  private malformed;
103
103
  private list;
104
+ /** Rows of a market list with one status, or every row for "all". */
105
+ private byStatus;
104
106
  /** Resolve a .csd name for DISPLAY (server-trusted; see the module trust note). null = unknown/invalid name. */
105
107
  resolve(name: string): Promise<NameResolution | null>;
106
108
  /** Full detail for one name (lease, lock, resting offer, recapture). null = not registered/invalid. */
@@ -116,12 +118,19 @@ declare class NamesClient {
116
118
  names(): Promise<unknown[]>;
117
119
  /** All deployed tokens. Throws a typed HttpError if any row has a malformed ticker, decimals or amount. */
118
120
  tokens(): Promise<TokenInfo[]>;
119
- /** One token by ticker. null = unknown/invalid ticker. Throws a typed HttpError on a malformed body. */
121
+ /** One token by ticker. null = unknown/invalid ticker. Throws a typed HttpError on a malformed body or a
122
+ * record for another ticker. */
120
123
  token(ticker: string): Promise<TokenInfo | null>;
121
- /** Open offers (the market's display list). */
122
- offers(): Promise<unknown[]>;
123
- /** Open bids. */
124
- bids(): Promise<unknown[]>;
124
+ /** Offers with one status, open ones by default (the market's display list). `{ status: "all" }` returns every
125
+ * offer the server holds, filled, cancelled and expired ones included. */
126
+ offers(opts?: {
127
+ status?: OfferStatus | "all";
128
+ }): Promise<unknown[]>;
129
+ /** Bids with one status, open ones by default. `{ status: "all" }` returns every bid the server holds, done,
130
+ * cancelled and expired ones included. */
131
+ bids(opts?: {
132
+ status?: BidStatus | "all";
133
+ }): Promise<unknown[]>;
125
134
  /** The full canonical CairnX state (large; prefer the scoped reads above). */
126
135
  state(): Promise<unknown>;
127
136
  }
@@ -189,7 +198,7 @@ declare function preverifyOffer(opts: {
189
198
 
190
199
  /** Stable machine-readable error codes (branch on these, not the message text). */
191
200
  type CairnErrorCode = "NOT_INSTALLED" | "USER_REJECTED" | "WALLET_LOCKED" | "UNSUPPORTED_METHOD" | "HTTP_ERROR" | "CONTENT_VERIFICATION" | "CONTENT_NOT_JSON" | "ACCOUNT_CHANGED" | "FIRST_PARTY_ONLY" | "RATE_LIMITED" | "APPROVAL_CLOSED" | "FORBIDDEN" | "INTERNAL" | "GHOST_INPUTS_SKIPPED" | "VERIFY_UNAVAILABLE" | "VERIFY_TAMPER" | "INSUFFICIENT" | "TOO_MANY_INPUTS" | "FEE_TOO_LOW" | "FEE_CAP" | "BAD_FEE" | "ZERO_ADDR_REFUSED" | "BAD_OUTPUTS" | "NO_OUTPUTS" | "BAD_REQUEST" | "OFFER_UNKNOWN" | "FILL_UNSAFE" | "FILL_WRONG_TARGET" | "SOURCE_DIVERGENCE" | "SUBMIT_REJECTED" | "SUBMIT_MAYBE_INFLIGHT" | "SUBMIT_DUPLICATE" | "NOTHING_TO_CONSOLIDATE" | "OFFER_SOURCE_CONFLICT" | "FILL_LEGACY_SUNSET" | "VERIFY_HORIZON" | "VERIFY_UNREPRESENTABLE" | "WALLET_UNAVAILABLE" | "REVIEW_REQUIRED" | "UNKNOWN";
192
- declare const SDK_VERSION = "0.7.0";
201
+ declare const SDK_VERSION = "0.8.0";
193
202
  interface CairnErrorOptions {
194
203
  code?: CairnErrorCode;
195
204
  docsPath?: string;
@@ -209,7 +218,7 @@ declare class CairnError extends Error {
209
218
  /** Relative docs path (under the SDK docs base), if any. */
210
219
  readonly docsPath?: string;
211
220
  /** The cairn-sdk version that produced this error. */
212
- readonly version = "0.7.0";
221
+ readonly version = "0.8.0";
213
222
  /**
214
223
  * Whether a dApp may safely AUTO-retry this failure. `false` for fund-safety refusals and user-intent
215
224
  * declines (the default); `true` only for provably transient infra faults where nothing was signed.
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ import { IndexerClient } from './indexer.js';
5
5
  export { InclusionResult, IndexEvent, MerkleProof, StreamHandle, StreamHandlers, TrustLevel } from './indexer.js';
6
6
  import { ContentClient } from './content.js';
7
7
  export { ContentClientOptions, PreparedContent } from './content.js';
8
- import { NameState, MintedProvenOfferTerms } from '@inversealtruism/cairnx-core';
8
+ import { NameState, OfferStatus, BidStatus, MintedProvenOfferTerms } from '@inversealtruism/cairnx-core';
9
9
  export { FillEndorsement, FillOutputPlan, FillPreview, FillSafety, MintedProvenOfferTerms, ProvenOfferTerms, bindOfferTerms, feeBpsAt, fillEndorsement, fillIsSafe, fillOutputPlan, previewFill, requiredFillOutputs } from '@inversealtruism/cairnx-core';
10
10
  import { WalletConnection, DetectOptions } from './connect.js';
11
11
  export { AttestParams, CairnProvider, ConnectResult, FillParams, ProposeParams, ProviderDetail, ProviderInfo, SealClaimParams, SendParams, SignInResult, SiwcParams, SiwcResult, TxResult, WalletCapabilities, WalletPermission, connect, detectProvider, discoverProviders, getWallet, isInstalled } from './connect.js';
@@ -16,7 +16,7 @@ import '@inversealtruism/csd-tx';
16
16
  import '@inversealtruism/csd-codec';
17
17
  import '@inversealtruism/csd-crypto';
18
18
 
19
- /** `/cairnx/resolve/:name` — the display-path name→address resolution. */
19
+ /** `/cairnx/resolve/:name`: the display-path name→address resolution. */
20
20
  interface NameResolution {
21
21
  ok: boolean;
22
22
  name: string;
@@ -28,7 +28,7 @@ interface NameResolution {
28
28
  owner?: string;
29
29
  tipHeight?: number;
30
30
  }
31
- /** `/cairnx/name/:name` — full name detail (lease, lock, resting offer, recapture window). */
31
+ /** `/cairnx/name/:name`: full name detail (lease, lock, resting offer, recapture window). */
32
32
  interface NameDetail {
33
33
  name: string;
34
34
  owner: string;
@@ -101,6 +101,8 @@ declare class NamesClient {
101
101
  constructor(http: Http);
102
102
  private malformed;
103
103
  private list;
104
+ /** Rows of a market list with one status, or every row for "all". */
105
+ private byStatus;
104
106
  /** Resolve a .csd name for DISPLAY (server-trusted; see the module trust note). null = unknown/invalid name. */
105
107
  resolve(name: string): Promise<NameResolution | null>;
106
108
  /** Full detail for one name (lease, lock, resting offer, recapture). null = not registered/invalid. */
@@ -116,12 +118,19 @@ declare class NamesClient {
116
118
  names(): Promise<unknown[]>;
117
119
  /** All deployed tokens. Throws a typed HttpError if any row has a malformed ticker, decimals or amount. */
118
120
  tokens(): Promise<TokenInfo[]>;
119
- /** One token by ticker. null = unknown/invalid ticker. Throws a typed HttpError on a malformed body. */
121
+ /** One token by ticker. null = unknown/invalid ticker. Throws a typed HttpError on a malformed body or a
122
+ * record for another ticker. */
120
123
  token(ticker: string): Promise<TokenInfo | null>;
121
- /** Open offers (the market's display list). */
122
- offers(): Promise<unknown[]>;
123
- /** Open bids. */
124
- bids(): Promise<unknown[]>;
124
+ /** Offers with one status, open ones by default (the market's display list). `{ status: "all" }` returns every
125
+ * offer the server holds, filled, cancelled and expired ones included. */
126
+ offers(opts?: {
127
+ status?: OfferStatus | "all";
128
+ }): Promise<unknown[]>;
129
+ /** Bids with one status, open ones by default. `{ status: "all" }` returns every bid the server holds, done,
130
+ * cancelled and expired ones included. */
131
+ bids(opts?: {
132
+ status?: BidStatus | "all";
133
+ }): Promise<unknown[]>;
125
134
  /** The full canonical CairnX state (large; prefer the scoped reads above). */
126
135
  state(): Promise<unknown>;
127
136
  }
@@ -189,7 +198,7 @@ declare function preverifyOffer(opts: {
189
198
 
190
199
  /** Stable machine-readable error codes (branch on these, not the message text). */
191
200
  type CairnErrorCode = "NOT_INSTALLED" | "USER_REJECTED" | "WALLET_LOCKED" | "UNSUPPORTED_METHOD" | "HTTP_ERROR" | "CONTENT_VERIFICATION" | "CONTENT_NOT_JSON" | "ACCOUNT_CHANGED" | "FIRST_PARTY_ONLY" | "RATE_LIMITED" | "APPROVAL_CLOSED" | "FORBIDDEN" | "INTERNAL" | "GHOST_INPUTS_SKIPPED" | "VERIFY_UNAVAILABLE" | "VERIFY_TAMPER" | "INSUFFICIENT" | "TOO_MANY_INPUTS" | "FEE_TOO_LOW" | "FEE_CAP" | "BAD_FEE" | "ZERO_ADDR_REFUSED" | "BAD_OUTPUTS" | "NO_OUTPUTS" | "BAD_REQUEST" | "OFFER_UNKNOWN" | "FILL_UNSAFE" | "FILL_WRONG_TARGET" | "SOURCE_DIVERGENCE" | "SUBMIT_REJECTED" | "SUBMIT_MAYBE_INFLIGHT" | "SUBMIT_DUPLICATE" | "NOTHING_TO_CONSOLIDATE" | "OFFER_SOURCE_CONFLICT" | "FILL_LEGACY_SUNSET" | "VERIFY_HORIZON" | "VERIFY_UNREPRESENTABLE" | "WALLET_UNAVAILABLE" | "REVIEW_REQUIRED" | "UNKNOWN";
192
- declare const SDK_VERSION = "0.7.0";
201
+ declare const SDK_VERSION = "0.8.0";
193
202
  interface CairnErrorOptions {
194
203
  code?: CairnErrorCode;
195
204
  docsPath?: string;
@@ -209,7 +218,7 @@ declare class CairnError extends Error {
209
218
  /** Relative docs path (under the SDK docs base), if any. */
210
219
  readonly docsPath?: string;
211
220
  /** The cairn-sdk version that produced this error. */
212
- readonly version = "0.7.0";
221
+ readonly version = "0.8.0";
213
222
  /**
214
223
  * Whether a dApp may safely AUTO-retry this failure. `false` for fund-safety refusals and user-intent
215
224
  * declines (the default); `true` only for provably transient infra faults where nothing was signed.
package/dist/index.js CHANGED
@@ -4,13 +4,13 @@ import {
4
4
  } from "./chunk-C452UODV.js";
5
5
  import {
6
6
  ContentClient
7
- } from "./chunk-TDXGYP3G.js";
7
+ } from "./chunk-DTPADO7N.js";
8
8
  import {
9
9
  IndexerClient
10
10
  } from "./chunk-CI7YODPP.js";
11
11
  import {
12
12
  CairnController
13
- } from "./chunk-XOGOSGC6.js";
13
+ } from "./chunk-JXBO4LV6.js";
14
14
  import {
15
15
  WalletConnection,
16
16
  connect,
@@ -18,7 +18,7 @@ import {
18
18
  discoverProviders,
19
19
  getWallet,
20
20
  isInstalled
21
- } from "./chunk-4FFLCCL4.js";
21
+ } from "./chunk-WWO3FXHG.js";
22
22
  import {
23
23
  CairnError,
24
24
  ContentVerificationError,
@@ -32,7 +32,7 @@ import {
32
32
  errorCode,
33
33
  mapProviderError,
34
34
  mapSubmitResultError
35
- } from "./chunk-PBKDSDID.js";
35
+ } from "./chunk-UAPWZZQK.js";
36
36
 
37
37
  // src/http.ts
38
38
  var DEFAULT_MAX_BYTES = 16 * 1024 * 1024;
@@ -203,9 +203,10 @@ function errText(body) {
203
203
  }
204
204
 
205
205
  // src/names.ts
206
- import { NAME_RE, RESERVED_NAMES, TICKER_RE, AMOUNT_RE, HASH_RE } from "@inversealtruism/cairnx-core";
206
+ import { NAME_RE, RESERVED_NAMES, TICKER_RE, HASH_RE, MAX_TOKEN_DECIMALS, parseAmount } from "@inversealtruism/cairnx-core";
207
207
  var nameOk = (n) => typeof n === "string" && NAME_RE.test(n) && !RESERVED_NAMES.has(n);
208
- var amountOk = (v) => typeof v === "string" && AMOUNT_RE.test(v);
208
+ var amountOk = (v) => parseAmount(v, { allowZero: true }) !== null;
209
+ var decimalsOk = (d) => Number.isSafeInteger(d) && d >= 0 && d <= MAX_TOKEN_DECIMALS;
209
210
  var heightOk = (v) => Number.isSafeInteger(v) && v >= 0;
210
211
  function nameHistoryProblem(h, name) {
211
212
  if (!h || typeof h !== "object" || Array.isArray(h)) return "not an object";
@@ -222,14 +223,15 @@ function nameHistoryProblem(h, name) {
222
223
  }
223
224
  return null;
224
225
  }
225
- function tokenProblem(t) {
226
+ function tokenProblem(t, ticker) {
226
227
  if (!t || typeof t !== "object") return "not an object";
227
228
  const r = t;
228
229
  if (typeof r.ticker !== "string" || !TICKER_RE.test(r.ticker)) return "bad ticker";
229
- if (!Number.isSafeInteger(r.decimals) || r.decimals < 0) return `bad decimals (${String(r.decimals)})`;
230
- if (!amountOk(r.supply)) return "supply is not a base-unit integer string";
231
- if (!amountOk(r.minted)) return "minted is not a base-unit integer string";
232
- if (r.mintLimit !== void 0 && !amountOk(r.mintLimit)) return "mintLimit is not a base-unit integer string";
230
+ if (ticker !== void 0 && r.ticker !== ticker) return `ticker is ${JSON.stringify(r.ticker)}, expected ${JSON.stringify(ticker)}`;
231
+ if (!decimalsOk(r.decimals)) return `bad decimals (${String(r.decimals)}), not an integer in the consensus range`;
232
+ if (!amountOk(r.supply)) return "supply is not a base-unit integer string within MAX_AMOUNT";
233
+ if (!amountOk(r.minted)) return "minted is not a base-unit integer string within MAX_AMOUNT";
234
+ if (r.mintLimit !== void 0 && !amountOk(r.mintLimit)) return "mintLimit is not a base-unit integer string within MAX_AMOUNT";
233
235
  return null;
234
236
  }
235
237
  var NamesClient = class {
@@ -240,11 +242,17 @@ var NamesClient = class {
240
242
  malformed(path, what) {
241
243
  return new HttpError(200, this.http.url(path), `malformed response: ${what}`);
242
244
  }
243
- async list(path) {
244
- const body = await this.http.getJson(path);
245
+ async list(path, query) {
246
+ const body = await this.http.getJson(path, query);
245
247
  if (!Array.isArray(body)) throw this.malformed(path, "expected an array");
246
248
  return body;
247
249
  }
250
+ /** Rows of a market list with one status, or every row for "all". */
251
+ async byStatus(path, status) {
252
+ if (status === "all") return this.list(path);
253
+ const rows = await this.list(path, { status });
254
+ return rows.filter((r) => r?.status === status);
255
+ }
248
256
  /** Resolve a .csd name for DISPLAY (server-trusted; see the module trust note). null = unknown/invalid name. */
249
257
  async resolve(name) {
250
258
  if (!nameOk(name)) return null;
@@ -315,7 +323,8 @@ var NamesClient = class {
315
323
  });
316
324
  return rows;
317
325
  }
318
- /** One token by ticker. null = unknown/invalid ticker. Throws a typed HttpError on a malformed body. */
326
+ /** One token by ticker. null = unknown/invalid ticker. Throws a typed HttpError on a malformed body or a
327
+ * record for another ticker. */
319
328
  async token(ticker) {
320
329
  if (typeof ticker !== "string" || !TICKER_RE.test(ticker)) return null;
321
330
  const path = `/trade/api/cairnx/token/${ticker}`;
@@ -326,17 +335,19 @@ var NamesClient = class {
326
335
  if (e?.status === 404) return null;
327
336
  throw e;
328
337
  }
329
- const p = tokenProblem(t);
338
+ const p = tokenProblem(t, ticker);
330
339
  if (p) throw this.malformed(path, p);
331
340
  return t;
332
341
  }
333
- /** Open offers (the market's display list). */
334
- offers() {
335
- return this.list(`/trade/api/cairnx/offers`);
342
+ /** Offers with one status, open ones by default (the market's display list). `{ status: "all" }` returns every
343
+ * offer the server holds, filled, cancelled and expired ones included. */
344
+ offers(opts = {}) {
345
+ return this.byStatus(`/trade/api/cairnx/offers`, opts.status ?? "open");
336
346
  }
337
- /** Open bids. */
338
- bids() {
339
- return this.list(`/trade/api/cairnx/bids`);
347
+ /** Bids with one status, open ones by default. `{ status: "all" }` returns every bid the server holds, done,
348
+ * cancelled and expired ones included. */
349
+ bids(opts = {}) {
350
+ return this.byStatus(`/trade/api/cairnx/bids`, opts.status ?? "open");
340
351
  }
341
352
  /** The full canonical CairnX state (large; prefer the scoped reads above). */
342
353
  state() {
@@ -509,10 +520,10 @@ async function preverifyOffer(opts) {
509
520
  return { ok: false, trust: "verified", reason: "the served offer is missing seller (the rebate recipient) - pass the resolver's full offer object, or omit servedOffer", ...base };
510
521
  if (String(so.seller).toLowerCase() !== seller)
511
522
  return { ok: false, trust: "verified", reason: "the served seller doesn't match the offer's on-chain author (a lying resolver may be redirecting your payment)", ...base };
512
- if (bindOfferTerms(opts.servedOffer, terms, { give: true, wantType: true }))
513
- return { ok: false, trust: "verified", reason: "the served terms don't match the offer's on-chain record (fee, rebate, partial sizing, the give, or the want type - a lying resolver could mis-size or re-route the fill, redirecting or burning your payment)", ...base };
514
523
  if (isTokenWant(offerRec.want) && (String(so?.want?.ticker) !== String(offerRec.want.ticker) || String(so?.want?.amount) !== String(offerRec.want.amount)))
515
524
  return { ok: false, trust: "verified", reason: "the served want ticker/amount don't match the offer's on-chain record (a lying resolver could bait-and-switch the token price)", ...base };
525
+ if (bindOfferTerms(opts.servedOffer, terms, { give: true, wantType: true }))
526
+ return { ok: false, trust: "verified", reason: "the served terms don't match the offer's on-chain record (fee, rebate, partial sizing, the give, or the want type - a lying resolver could mis-size or re-route the fill, redirecting or burning your payment)", ...base };
516
527
  }
517
528
  if (sumsMismatch) return { ok: false, trust: "verified", reason: sumsMismatch, ...base };
518
529
  return { ok: true, trust: "verified", ...base };
package/dist/react.cjs CHANGED
@@ -26,7 +26,7 @@ __export(react_exports, {
26
26
  module.exports = __toCommonJS(react_exports);
27
27
 
28
28
  // src/errors.ts
29
- var SDK_VERSION = "0.7.0";
29
+ var SDK_VERSION = "0.8.0";
30
30
  var DOCS_BASE = "https://cairn-substrate.com/docs/sdk";
31
31
  var CairnError = class extends Error {
32
32
  /** Stable machine code for programmatic branching. */
package/dist/react.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  CairnController
3
- } from "./chunk-XOGOSGC6.js";
4
- import "./chunk-4FFLCCL4.js";
5
- import "./chunk-PBKDSDID.js";
3
+ } from "./chunk-JXBO4LV6.js";
4
+ import "./chunk-WWO3FXHG.js";
5
+ import "./chunk-UAPWZZQK.js";
6
6
 
7
7
  // src/react.ts
8
8
  function createCairnHooks(React, controllerOrOpts) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inversealtruism/cairn-sdk",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "The Compute Substrate dApp kit: one cohesive toolkit to connect the Cairn Wallet, read the chain, the indexer and .csd names, verify self-certifying content, and resolve .csd names. Composes the published csd-* primitives.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -49,16 +49,16 @@
49
49
  "README.md"
50
50
  ],
51
51
  "dependencies": {
52
- "@inversealtruism/cairnx-core": "0.2.0",
53
- "@inversealtruism/csd-client": "0.1.18",
52
+ "@inversealtruism/cairnx-core": "0.2.2",
53
+ "@inversealtruism/csd-client": "0.1.19",
54
54
  "@inversealtruism/csd-codec": "0.1.18",
55
- "@inversealtruism/csd-crypto": "0.1.18",
56
- "@inversealtruism/csd-light": "0.1.23",
57
- "@inversealtruism/csd-tx": "0.1.21"
55
+ "@inversealtruism/csd-crypto": "0.1.20",
56
+ "@inversealtruism/csd-light": "0.1.25",
57
+ "@inversealtruism/csd-tx": "0.1.23"
58
58
  },
59
59
  "devDependencies": {
60
60
  "@biomejs/biome": "2.5.15",
61
- "@inversealtruism/csd-siwc": "0.1.19",
61
+ "@inversealtruism/csd-siwc": "0.1.21",
62
62
  "@types/node": "22.19.20",
63
63
  "esbuild": "0.28.2",
64
64
  "playwright-core": "1.60.0",