@geeqdev/geeq-sdk 0.2.1 → 0.2.2

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.
Files changed (52) hide show
  1. package/README.md +12 -2
  2. package/dist/browser/geeq_core_bg.wasm +0 -0
  3. package/dist/browser/index.js +12468 -0
  4. package/dist/browser/index.js.map +1 -0
  5. package/dist/node/geeq_core_bg.wasm +0 -0
  6. package/dist/node/index.cjs +12559 -0
  7. package/dist/node/index.cjs.map +1 -0
  8. package/dist/node/index.js +12469 -0
  9. package/dist/node/index.js.map +1 -0
  10. package/dist/types/index.d.cts +2224 -0
  11. package/dist/types/index.d.ts +2224 -0
  12. package/package.json +8 -3
  13. package/.nvmrc +0 -1
  14. package/CONTRIBUTING.md +0 -20
  15. package/example/.env.example +0 -4
  16. package/example/README.md +0 -304
  17. package/example/USAGE.md +0 -74
  18. package/example/index.html +0 -12
  19. package/example/package-lock.json +0 -1557
  20. package/example/package.json +0 -25
  21. package/example/scripts/browser-check.mjs +0 -1421
  22. package/example/scripts/match-derivation.mjs +0 -65
  23. package/example/src/App.vue +0 -138
  24. package/example/src/components/AuthorityPanel.vue +0 -277
  25. package/example/src/components/ChainPanel.vue +0 -267
  26. package/example/src/components/ChainState.vue +0 -59
  27. package/example/src/components/CopyButton.vue +0 -25
  28. package/example/src/components/CreateAssetPanel.vue +0 -313
  29. package/example/src/components/DigitalAssetsPanel.vue +0 -68
  30. package/example/src/components/EvidencePanel.vue +0 -396
  31. package/example/src/components/ExplorerPanel.vue +0 -119
  32. package/example/src/components/KeypairPanel.vue +0 -182
  33. package/example/src/components/PhrasePanel.vue +0 -105
  34. package/example/src/components/SocketWarning.vue +0 -18
  35. package/example/src/components/StepGate.vue +0 -24
  36. package/example/src/components/TransferPanel.vue +0 -416
  37. package/example/src/components/TxDeadline.vue +0 -19
  38. package/example/src/components/TxFee.vue +0 -31
  39. package/example/src/components/TxStatus.vue +0 -105
  40. package/example/src/composables/useSocketConnection.ts +0 -80
  41. package/example/src/env.d.ts +0 -12
  42. package/example/src/main.ts +0 -5
  43. package/example/src/pennies.ts +0 -7
  44. package/example/src/style.css +0 -149
  45. package/example/tsconfig.json +0 -19
  46. package/example/vite.config.ts +0 -26
  47. package/jest.config.js +0 -18
  48. package/scripts/copy-wasm.mjs +0 -21
  49. package/scripts/smoke.mjs +0 -382
  50. package/tsconfig.build.json +0 -8
  51. package/tsconfig.json +0 -21
  52. package/tsup.config.ts +0 -60
@@ -0,0 +1,2224 @@
1
+ /** Account ids and public keys are both 32 bytes. */
2
+ declare const ACCOUNT_ID_BYTES = 32;
3
+ declare const PUBLIC_KEY_BYTES = 32;
4
+ /**
5
+ * Any way of naming an account that does not need the engine.
6
+ *
7
+ * Structural on purpose: a {@link GeeqKeypair} already carries `accountId`, so it
8
+ * satisfies this without the crypto layer importing the keypair type and without the
9
+ * caller unwrapping anything.
10
+ */
11
+ type AccountRef = Uint8Array | {
12
+ readonly accountId: Uint8Array;
13
+ } | {
14
+ readonly publicKey: Uint8Array;
15
+ };
16
+ /**
17
+ * Derive the 32-byte account id for a public key.
18
+ *
19
+ * This is a pure hash — it needs no engine and no network, so it works before
20
+ * {@link initGeeq} has resolved.
21
+ */
22
+ declare function accountIdFromPublicKey(publicKey: Uint8Array): Uint8Array;
23
+
24
+ /** Which coin record subtype an account is. Only `'asset'` may sign an attestation. */
25
+ type GeeqCoinRecordKind = 'asset' | 'hl' | 'micro' | 'unknown';
26
+ /** One coin account record, as plain values. */
27
+ interface GeeqCoinAccount {
28
+ readonly accountId: Uint8Array;
29
+ readonly kind: GeeqCoinRecordKind;
30
+ /**
31
+ * `undefined` for a hash-lock record, which carries no nonce field at all, and for a
32
+ * record whose subtype could not be read.
33
+ */
34
+ readonly nonce: number | undefined;
35
+ /** Balance in pennies. Use `formatGeeq` to show it. */
36
+ readonly balance: bigint;
37
+ /** The NFTs it holds, in the order its record lists them. Read for coin-asset records only. */
38
+ readonly nfts: readonly GeeqHeldNft[];
39
+ }
40
+ /** One NFT an account holds, as its record lists it. */
41
+ interface GeeqHeldNft {
42
+ /** The NFT's 32-byte id. */
43
+ readonly id: Uint8Array;
44
+ /** A counterparty NFT moves only with a counterparty transaction, never a plain transfer. */
45
+ readonly counterparty: boolean;
46
+ }
47
+ /** What we keep per account. The id is the map key, so it is not repeated here. */
48
+ interface CoinAccountFacts {
49
+ readonly kind: GeeqCoinRecordKind;
50
+ readonly nonce: number | undefined;
51
+ readonly balance: bigint;
52
+ readonly nfts: readonly GeeqHeldNft[];
53
+ }
54
+
55
+ /** Which kind of proxy a record is. Only `'nft-mint'` is read further. */
56
+ type GeeqProxyKind = 'nft-mint' | 'token-mint' | 'funding' | 'multisig' | 'dce' | 'unknown';
57
+ /** One proxy account record, as plain values. */
58
+ interface GeeqProxyAccount {
59
+ readonly accountId: Uint8Array;
60
+ readonly kind: GeeqProxyKind;
61
+ /**
62
+ * The proxy's own nonce. A mint through it spends this one, not the admin's — a proxy
63
+ * carries its nonce on the record itself.
64
+ */
65
+ readonly nonce: number;
66
+ /** Balance in pennies. Use `formatGeeq` to show it. */
67
+ readonly balance: bigint;
68
+ /** The account id of the only key that can sign for it. Read for NFT mint proxies only. */
69
+ readonly admin: Uint8Array | undefined;
70
+ }
71
+ /** What we keep per proxy. The id is the map key, so it is not repeated here. */
72
+ type ProxyAccountFacts = Omit<GeeqProxyAccount, 'accountId'>;
73
+
74
+ /**
75
+ * Where the WebAssembly engine should be loaded from. Leave it unset and the SDK
76
+ * uses the copy shipped beside its own bundle, which every common bundler emits
77
+ * as a hashed asset automatically.
78
+ */
79
+ type WasmSource = string | URL | Request | Response | ArrayBuffer | Uint8Array | WebAssembly.Module;
80
+ interface InitOptions {
81
+ /** Override where `geeq_core_bg.wasm` is fetched from. */
82
+ readonly wasm?: WasmSource;
83
+ }
84
+ /**
85
+ * The kind of ledger record an address points at. Most applications only ever
86
+ * need `'coin-asset'`, which is the default and is what a normal user account is.
87
+ */
88
+ type GeeqRecordType = 'coin-asset' | 'coin-hl' | 'coin-micro' | 'app-counterparty' | 'app-dce' | 'app-nft' | 'app-stackable' | 'proxy-nft-mint' | 'proxy-funding' | 'proxy-multisig' | 'proxy-dce' | 'proxy-token-mint' | 'gt-nwa' | 'param-boolean' | 'param-count-2' | 'param-count-8' | 'sys';
89
+ /** Which coin family a derivation path belongs to. */
90
+ type GeeqCoinType = 'mainnet' | 'testnet';
91
+ interface WalletAddressOptions {
92
+ /** The chain this address belongs to. Addresses are not portable across chains. */
93
+ readonly chainNumber: number;
94
+ /** Defaults to `'coin-asset'` — an ordinary user account. */
95
+ readonly recordType?: GeeqRecordType;
96
+ }
97
+ interface DecodedWalletAddress {
98
+ /** The address that was decoded, unchanged. */
99
+ readonly address: string;
100
+ readonly chainNumber: number;
101
+ readonly recordType: GeeqRecordType;
102
+ /** The 32-byte account id this address points at. */
103
+ readonly accountId: Uint8Array;
104
+ }
105
+ /** Options for deriving a keypair from a BIP-39 mnemonic along the Geeq path. */
106
+ interface MnemonicOptions {
107
+ /** Optional BIP-39 passphrase. Defaults to an empty string. */
108
+ readonly passphrase?: string;
109
+ /** Defaults to `'coin-asset'`. */
110
+ readonly recordType?: GeeqRecordType;
111
+ /** Defaults to `'mainnet'`. */
112
+ readonly coinType?: GeeqCoinType;
113
+ /** Defaults to 0. */
114
+ readonly account?: number;
115
+ /** Defaults to 0. */
116
+ readonly addressIndex?: number;
117
+ }
118
+ /**
119
+ * An Ed25519 keypair plus the identifiers derived from it.
120
+ *
121
+ * `privateKey` is raw secret material. Never log it, never put it in an error
122
+ * message, and never send it anywhere. Call {@link GeeqKeypair.dispose} when done
123
+ * to release the engine-side copy.
124
+ */
125
+ interface GeeqKeypair {
126
+ /** 32-byte Ed25519 public key. */
127
+ readonly publicKey: Uint8Array;
128
+ /** 32-byte Ed25519 secret key. Secret material. */
129
+ readonly privateKey: Uint8Array;
130
+ /** 32-byte account id derived from the public key. */
131
+ readonly accountId: Uint8Array;
132
+ /** The base58 wallet address for this keypair on a given chain. */
133
+ getAddress(options: WalletAddressOptions): string;
134
+ /** Sign a message. Throws once {@link GeeqKeypair.dispose} has been called. */
135
+ sign(message: Uint8Array): Uint8Array;
136
+ /** Release the engine-side keypair. The byte fields stay readable afterwards. */
137
+ dispose(): void;
138
+ }
139
+ /** The metadata limits this chain publishes in its own ledger. */
140
+ interface GeeqMetadataLimits {
141
+ /** Maximum NUMBER of metadata entries. From the `metadataVarNum` parameter. */
142
+ readonly maxEntries: number;
143
+ /** Maximum bytes in ONE entry. There is no aggregate cap. From `metadataVarBytes`. */
144
+ readonly maxBytesPerEntry: number;
145
+ }
146
+ /**
147
+ * The live chain values a transaction is pinned to.
148
+ *
149
+ * `blockNumber` is the block being *asked for*, which is the ledger's last committed
150
+ * block plus one. Read it from a node with {@link GeeqChain}, or supply it yourself for
151
+ * replay and offline signing.
152
+ */
153
+ interface ChainStateInput {
154
+ /** The chain this transaction targets — the ledger's own chain number. */
155
+ readonly chainNumber: number;
156
+ /** The block it asks to be included in: the ledger's block **+ 1**. */
157
+ readonly blockNumber: number;
158
+ /** The receiving node's own number, from the net actor list in the ledger. */
159
+ readonly nodeNumber: number;
160
+ /** The signing account's current nonce, from its coin account record. */
161
+ readonly nonce: number;
162
+ /** When present, metadata is checked against it before anything is signed. */
163
+ readonly metadataLimits?: GeeqMetadataLimits | undefined;
164
+ /**
165
+ * The chain's `geeqNorm` parameter, which every fee is a multiple of. When present, the
166
+ * built transaction carries its {@link GeeqFee}.
167
+ */
168
+ readonly geeqNorm?: bigint | undefined;
169
+ /**
170
+ * The node this state describes, and therefore the only node that will accept a
171
+ * transaction built from it — `nodeNumber` is checked with strict equality.
172
+ */
173
+ readonly nodeUrl?: string | undefined;
174
+ /**
175
+ * N, the ledger block this was read from. Present only when a chain produced it —
176
+ * there is nothing to report when the caller supplied the numbers.
177
+ */
178
+ readonly ledgerBlockNumber?: number | undefined;
179
+ /** `Date.now()` at the read. Present only when a chain produced it. */
180
+ readonly readAt?: number | undefined;
181
+ }
182
+ /**
183
+ * Chain state read from a node, carrying where and when it came from.
184
+ *
185
+ * The same fields as {@link ChainStateInput}, with the provenance ones narrowed from
186
+ * optional to always present — a chain can always say which ledger block and which node
187
+ * it read.
188
+ */
189
+ interface GeeqChainState extends ChainStateInput {
190
+ /** N — the ledger's last committed block. {@link blockNumber} is this plus one. */
191
+ readonly ledgerBlockNumber: number;
192
+ /** `Date.now()` when the ledger was read, so callers can see how stale this is. */
193
+ readonly readAt: number;
194
+ /** The node it was read from, and therefore the node it must be submitted to. */
195
+ readonly nodeUrl: string;
196
+ /** The signing account's balance, in pennies, as at this read. */
197
+ readonly balance: bigint;
198
+ /** The smallest transfer this chain accepts, in pennies, if it publishes one. */
199
+ readonly minimumTransfer: bigint | undefined;
200
+ /** The chain's `geeqNorm` parameter, if it publishes one. */
201
+ readonly geeqNorm: bigint | undefined;
202
+ }
203
+ /**
204
+ * What the chain charges for a transaction, in pennies, on top of any amount it sends.
205
+ *
206
+ * Worked out by the SDK the way the node works it out, from the signed bytes, so it is
207
+ * the exact figure rather than an estimate.
208
+ */
209
+ interface GeeqFee {
210
+ /** The whole fee: {@link data} plus {@link coin}. */
211
+ readonly total: bigint;
212
+ /** `geeqNorm` per byte of the signed transaction, and of its payload when it has one. */
213
+ readonly data: bigint;
214
+ /**
215
+ * A quarter of a percent of the amount sent, rounded down and capped at
216
+ * `geeqNorm × 10⁷`. Zero for an attestation, which sends no amount.
217
+ */
218
+ readonly coin: bigint;
219
+ /** How many bytes {@link data} charges for. */
220
+ readonly bytes: number;
221
+ }
222
+ /** What is being attested, independent of any chain state. */
223
+ interface AttestationContent {
224
+ /** The account that signs both the attestation and the transaction. */
225
+ readonly keypair: GeeqKeypair;
226
+ /** 32-byte hash of the content. The content itself never leaves the device. */
227
+ readonly attestHash: Uint8Array;
228
+ /**
229
+ * Optional labels stored alongside the hash, in the order given. Strings are encoded
230
+ * as UTF-8. Anything here is public once submitted.
231
+ */
232
+ readonly metadata?: readonly (string | Uint8Array)[];
233
+ }
234
+ /**
235
+ * The four chain values supplied explicitly.
236
+ *
237
+ * Kept for replay, deterministic tests and air-gapped signing, where there is no node
238
+ * to ask. Most callers should let {@link GeeqChain} read them instead — none of them can
239
+ * be derived offline, and a wrong one is rejected by the chain.
240
+ */
241
+ type AttestationTxFlatInput = AttestationContent & {
242
+ readonly chainNumber: number;
243
+ readonly blockNumber: number;
244
+ readonly nodeNumber: number;
245
+ readonly nonce: number;
246
+ readonly chainState?: never;
247
+ };
248
+ /** The same four values as one object, which is what a chain read produces. */
249
+ type AttestationTxStateInput = AttestationContent & {
250
+ readonly chainState: ChainStateInput;
251
+ readonly chainNumber?: never;
252
+ readonly blockNumber?: never;
253
+ readonly nodeNumber?: never;
254
+ readonly nonce?: never;
255
+ };
256
+ type AttestationTxInput = AttestationTxFlatInput | AttestationTxStateInput;
257
+ /**
258
+ * A signed attestation transaction, ready to submit. The SDK does not submit it — post
259
+ * {@link AttestationTx.transaction} and {@link AttestationTx.payload} to the node named
260
+ * by {@link AttestationTx.nodeUrl}, after handing it to `GeeqSocket.track` to hear the
261
+ * verdict.
262
+ */
263
+ interface AttestationTx {
264
+ /** base64 packed `UserAppTx`. Submit as the `transaction` field. */
265
+ readonly transaction: string;
266
+ /** base64 packed `Payload`. Submit as the `payload` field. */
267
+ readonly payload: string;
268
+ /** 32-byte BLAKE3 of the flattened payload, as embedded in the transaction. */
269
+ readonly refHashPayload: Uint8Array;
270
+ /**
271
+ * 32-byte BLAKE3 of the signed, flattened transaction — how a node identifies it, and
272
+ * the `refHash` its status events carry. `transactionIdOf(tx)` gives it as the base64
273
+ * string a {@link GeeqSocket} matches on.
274
+ */
275
+ readonly transactionHash: Uint8Array;
276
+ /** The signer's 32-byte public key — whose status events to subscribe to. */
277
+ readonly publicKey: Uint8Array;
278
+ /**
279
+ * What the chain charges the signing account for it. Undefined when the chain state
280
+ * carried no `geeqNorm`.
281
+ */
282
+ readonly fee: GeeqFee | undefined;
283
+ /** Exactly what this transaction is pinned to. Frozen. */
284
+ readonly chainState: ChainStateInput;
285
+ /**
286
+ * The last block a node takes it at: its target block plus 8. Once the chain's latest
287
+ * block is past this, every node refuses it as too old, and it has to be built and
288
+ * signed again. `GeeqChain.isExpired` checks.
289
+ */
290
+ readonly validUntilBlock: number;
291
+ /**
292
+ * Where this must be submitted, when a chain read chose the node.
293
+ *
294
+ * Not a convenience: the node checks that `nodeNumber` is its own, with strict
295
+ * equality, so posting to any other node is a guaranteed rejection. Undefined only
296
+ * when the caller supplied the four values themselves and the SDK never saw a node.
297
+ */
298
+ readonly nodeUrl?: string | undefined;
299
+ }
300
+ /** What is being transferred, independent of any chain state. */
301
+ interface CoinTransferContent {
302
+ /** The account paying. Must be a coin-asset account on this chain. */
303
+ readonly keypair: GeeqKeypair;
304
+ /** Who receives it. Resolved to a 32-byte account id. */
305
+ readonly to: AccountRef;
306
+ /**
307
+ * How much, in pennies. Use `toPennies` to get here from a figure. Never
308
+ * zero: the node reads a zero amount as the sender's whole balance.
309
+ */
310
+ readonly amount: bigint;
311
+ /** NFTs sent with it. Each must be one the sender holds, named once. */
312
+ readonly nfts?: readonly NftTransferEntry[];
313
+ }
314
+ /** One NFT a transfer moves. */
315
+ interface NftTransferEntry {
316
+ /** The NFT's 32-byte id. */
317
+ readonly id: Uint8Array;
318
+ /** Where it sits in the sender's list of NFTs, from 0 — as `GeeqCoinAccount.nfts` orders them. */
319
+ readonly position: number;
320
+ }
321
+ /** The four chain values supplied explicitly. For replay and air-gapped signing. */
322
+ type CoinTransferTxFlatInput = CoinTransferContent & {
323
+ readonly chainNumber: number;
324
+ readonly blockNumber: number;
325
+ readonly nodeNumber: number;
326
+ readonly nonce: number;
327
+ readonly chainState?: never;
328
+ };
329
+ /** The same four as one object, which is what a chain read produces. */
330
+ type CoinTransferTxStateInput = CoinTransferContent & {
331
+ readonly chainState: ChainStateInput;
332
+ readonly chainNumber?: never;
333
+ readonly blockNumber?: never;
334
+ readonly nodeNumber?: never;
335
+ readonly nonce?: never;
336
+ };
337
+ type CoinTransferTxInput = CoinTransferTxFlatInput | CoinTransferTxStateInput;
338
+ /**
339
+ * A signed coin transfer, ready to submit.
340
+ *
341
+ * Note there is no `payload`: a validation-layer transaction is self-contained, unlike an
342
+ * attestation. Post it with `coinTransferSubmitBody`, which sends it as type `uvt`, after
343
+ * handing it to `GeeqSocket.track` to hear the verdict.
344
+ */
345
+ interface CoinTransferTx {
346
+ /** base64 packed `UserValTx`. Submit as the `transaction` field. */
347
+ readonly transaction: string;
348
+ /**
349
+ * 32-byte BLAKE3 of the flattened transaction **without** its signature — how the chain
350
+ * identifies a validation-layer transaction, and the `refHash` its status events carry.
351
+ * Note this is the opposite of an attestation, whose hash includes the signature.
352
+ */
353
+ readonly transactionHash: Uint8Array;
354
+ /** The sender's 32-byte public key — whose status events to subscribe to. */
355
+ readonly publicKey: Uint8Array;
356
+ /** What was sent, in pennies. */
357
+ readonly amount: bigint;
358
+ /** The ids of the NFTs sent with it, if any. */
359
+ readonly nfts: readonly Uint8Array[];
360
+ /**
361
+ * What the chain charges the sender, on top of {@link amount}. Undefined when the chain
362
+ * state carried no `geeqNorm`.
363
+ */
364
+ readonly fee: GeeqFee | undefined;
365
+ /** The 32-byte account id it was sent to. */
366
+ readonly to: Uint8Array;
367
+ /** Exactly what this transaction is pinned to. Frozen. */
368
+ readonly chainState: ChainStateInput;
369
+ /**
370
+ * The last block a node takes it at: its target block plus 8. Once the chain's latest
371
+ * block is past this, every node refuses it as too old, and it has to be built and
372
+ * signed again. `GeeqChain.isExpired` checks.
373
+ */
374
+ readonly validUntilBlock: number;
375
+ /** Where it must be submitted, when a chain read chose the node. */
376
+ readonly nodeUrl?: string | undefined;
377
+ }
378
+ /**
379
+ * What a proxy creation needs. The chain values come as one object only — there is no
380
+ * flat form, since a proxy is always built against a ledger read.
381
+ */
382
+ interface NftMintProxyTxInput {
383
+ /**
384
+ * The account paying for the proxy, which also becomes its admin: the only key that
385
+ * can later mint through it. Must be a coin-asset account on this chain.
386
+ */
387
+ readonly keypair: GeeqKeypair;
388
+ /**
389
+ * Which of the admin's proxies this is, from 1. The proxy's id is derived from the
390
+ * admin's public key and this index, as the Geeq wallet derives it, so it must be one
391
+ * the admin has not used — `GeeqChain.findNftMintProxies` gives the next one.
392
+ */
393
+ readonly index: number;
394
+ /** Coin moved onto the proxy, in pennies. It pays for the mints made through it. */
395
+ readonly amount: bigint;
396
+ readonly chainState: ChainStateInput;
397
+ }
398
+ /**
399
+ * A signed transaction creating an NFT mint proxy, ready to submit.
400
+ *
401
+ * A validation-layer transaction, like a transfer: post it with `coinTransferSubmitBody`,
402
+ * after handing it to `GeeqSocket.track` to hear the verdict.
403
+ */
404
+ interface NftMintProxyTx {
405
+ /** base64 packed `UserValTx`. Submit as the `transaction` field. */
406
+ readonly transaction: string;
407
+ /** 32-byte BLAKE3 of the signature-excluded flatten — the `refHash` its status events carry. */
408
+ readonly transactionHash: Uint8Array;
409
+ /** The admin's 32-byte public key — whose status events to subscribe to. */
410
+ readonly publicKey: Uint8Array;
411
+ /** The coin moved onto the proxy, in pennies. */
412
+ readonly amount: bigint;
413
+ /** Which of the admin's proxies this is, from 1. */
414
+ readonly index: number;
415
+ /** The new proxy's 32-byte account id, derived from the admin's key and {@link index}. */
416
+ readonly proxyAccountId: Uint8Array;
417
+ /** The new proxy's wallet address on this chain, record type `proxy-nft-mint`. */
418
+ readonly proxyAddress: string;
419
+ /**
420
+ * What the chain charges the admin, on top of {@link amount}. Undefined when the chain
421
+ * state carried no `geeqNorm`.
422
+ */
423
+ readonly fee: GeeqFee | undefined;
424
+ /** Exactly what this transaction is pinned to. Frozen. */
425
+ readonly chainState: ChainStateInput;
426
+ /**
427
+ * The last block a node takes it at: its target block plus 8. Once the chain's latest
428
+ * block is past this, every node refuses it as too old, and it has to be built and
429
+ * signed again. `GeeqChain.isExpired` checks.
430
+ */
431
+ readonly validUntilBlock: number;
432
+ /** Where it must be submitted, when a chain read chose the node. */
433
+ readonly nodeUrl?: string | undefined;
434
+ }
435
+ /**
436
+ * What an NFT mint needs. The chain values come as one object only, as for a proxy.
437
+ */
438
+ interface NftMintTxInput {
439
+ /** The proxy's admin. It signs both the NFT and the transaction. */
440
+ readonly keypair: GeeqKeypair;
441
+ /** The 32-byte account id of the proxy minting it, which pays for it. */
442
+ readonly proxyAccountId: Uint8Array;
443
+ /** 32-byte hash of what the NFT stands for. The content itself never leaves the device. */
444
+ readonly attestHash: Uint8Array;
445
+ /** Optional public labels stored with the NFT, in the order given. */
446
+ readonly metadata?: readonly (string | Uint8Array)[];
447
+ /** Who receives the NFT. Resolved to a 32-byte account id. */
448
+ readonly to: AccountRef;
449
+ /** Coin the proxy sends to the receiver along with the NFT, in pennies. */
450
+ readonly amount: bigint;
451
+ /** Its `nonce` is the proxy's own, not the admin's. */
452
+ readonly chainState: ChainStateInput;
453
+ }
454
+ /**
455
+ * A signed NFT mint, ready to submit.
456
+ *
457
+ * An application-layer transaction, like an attestation: post `transaction` and `payload`
458
+ * with `attestationSubmitBody`, after handing it to `GeeqSocket.track` to hear the verdict.
459
+ */
460
+ interface NftMintTx {
461
+ /** base64 packed `UserAppTx`. Submit as the `transaction` field. */
462
+ readonly transaction: string;
463
+ /** base64 packed `Payload`. Submit as the `payload` field. */
464
+ readonly payload: string;
465
+ /** 32-byte BLAKE3 of the flattened payload, as embedded in the transaction. */
466
+ readonly refHashPayload: Uint8Array;
467
+ /** 32-byte BLAKE3 of the signed, flattened transaction — the `refHash` its status events carry. */
468
+ readonly transactionHash: Uint8Array;
469
+ /** The admin's 32-byte public key — whose status events to subscribe to. */
470
+ readonly publicKey: Uint8Array;
471
+ /** The new NFT's 32-byte id: the hash of its attestation subgroup, as the wallet derives it. */
472
+ readonly nftId: Uint8Array;
473
+ /** The proxy minting it. */
474
+ readonly proxyAccountId: Uint8Array;
475
+ /** The 32-byte account id receiving it. */
476
+ readonly to: Uint8Array;
477
+ /** The coin sent along with it, in pennies. */
478
+ readonly amount: bigint;
479
+ /**
480
+ * What the chain charges the proxy — not the admin — on top of {@link amount}. Undefined
481
+ * when the chain state carried no `geeqNorm`.
482
+ */
483
+ readonly fee: GeeqFee | undefined;
484
+ /** Exactly what this transaction is pinned to. Frozen. */
485
+ readonly chainState: ChainStateInput;
486
+ /**
487
+ * The last block a node takes it at: its target block plus 8. Once the chain's latest
488
+ * block is past this, every node refuses it as too old, and it has to be built and
489
+ * signed again. `GeeqChain.isExpired` checks.
490
+ */
491
+ readonly validUntilBlock: number;
492
+ /** Where it must be submitted, when a chain read chose the node. */
493
+ readonly nodeUrl?: string | undefined;
494
+ }
495
+ /** One of an admin's NFT mint proxies, as the ledger holds it. */
496
+ interface GeeqNftMintProxy extends GeeqProxyAccount {
497
+ /**
498
+ * Which of the admin's proxies it is, from 1. Undefined for a proxy whose id was not
499
+ * derived from the admin's key — one made with an id of its own.
500
+ */
501
+ readonly index: number | undefined;
502
+ /** Its wallet address on this chain, record type `proxy-nft-mint`. */
503
+ readonly address: string;
504
+ }
505
+ /** The NFT mint proxies one admin has, and the index its next one takes. */
506
+ interface GeeqNftMintProxies {
507
+ /** In index order; any without an index come last. */
508
+ readonly proxies: readonly GeeqNftMintProxy[];
509
+ /** One past the highest index in use, or 1 when there is none. */
510
+ readonly nextIndex: number;
511
+ }
512
+ /** An NFT an account holds, with the hash and labels it was minted with. */
513
+ interface GeeqNft extends GeeqHeldNft {
514
+ /** The hash of what it stands for. Undefined when the application layer ledger has no record of it. */
515
+ readonly attestHash: Uint8Array | undefined;
516
+ /** Its public labels as text, in order. */
517
+ readonly metadata: readonly string[];
518
+ }
519
+
520
+ /**
521
+ * Load the Geeq engine. Idempotent and single-flight: concurrent callers share one
522
+ * load, and repeat calls after success resolve immediately.
523
+ *
524
+ * You rarely need to call this yourself — every async entry point in the SDK awaits
525
+ * it first. Call it at startup if you would rather pay the cost up front.
526
+ */
527
+ declare function initGeeq(options?: InitOptions): Promise<void>;
528
+ /** True once the engine is loaded and the synchronous APIs are usable. */
529
+ declare function isGeeqReady(): boolean;
530
+
531
+ /** Create a brand new random keypair. Synchronous; requires a loaded engine. */
532
+ declare function generateKeypairSync(): GeeqKeypair;
533
+ /** Create a brand new random keypair, loading the engine if needed. */
534
+ declare function generateKeypair(): Promise<GeeqKeypair>;
535
+ /** Re-create a keypair from stored public and private key bytes. */
536
+ declare function importKeypairSync(publicKey: Uint8Array, privateKey: Uint8Array): GeeqKeypair;
537
+ /** Re-create a keypair from stored key bytes, loading the engine if needed. */
538
+ declare function importKeypair(publicKey: Uint8Array, privateKey: Uint8Array): Promise<GeeqKeypair>;
539
+ /** Recover a keypair from its 32-byte secret key alone. */
540
+ declare function keypairFromSecretKeySync(privateKey: Uint8Array): GeeqKeypair;
541
+ /** Recover a keypair from its secret key, loading the engine if needed. */
542
+ declare function keypairFromSecretKey(privateKey: Uint8Array): Promise<GeeqKeypair>;
543
+ /**
544
+ * Derive a keypair from a BIP-39 mnemonic along Geeq's standard derivation path.
545
+ *
546
+ * The same mnemonic and options always produce the same keypair, so this is how a
547
+ * wallet is restored on another device.
548
+ */
549
+ declare function keypairFromMnemonicSync(mnemonic: string, options?: MnemonicOptions): GeeqKeypair;
550
+ /** Derive a keypair from a mnemonic, loading the engine if needed. */
551
+ declare function keypairFromMnemonic(mnemonic: string, options?: MnemonicOptions): Promise<GeeqKeypair>;
552
+ /** Verify an Ed25519 signature. Requires a loaded engine. */
553
+ declare function verifySignature(publicKey: Uint8Array, signature: Uint8Array, message: Uint8Array): boolean;
554
+
555
+ /** Supported mnemonic lengths. 12 words is the common default; 24 is the strongest. */
556
+ type MnemonicWordCount = 12 | 15 | 18 | 21 | 24;
557
+ declare const MNEMONIC_WORD_COUNTS: readonly [12, 15, 18, 21, 24];
558
+ interface GenerateMnemonicOptions {
559
+ /** How many words to generate. Defaults to 12. */
560
+ readonly words?: MnemonicWordCount;
561
+ }
562
+ /**
563
+ * Generate a fresh BIP-39 recovery phrase, using the platform's cryptographically
564
+ * secure random source.
565
+ *
566
+ * Pure JavaScript — no engine needed, so this works before {@link initGeeq}
567
+ * resolves. Phrases are generated from the English wordlist, which is what
568
+ * {@link keypairFromMnemonic} expects.
569
+ *
570
+ * The phrase is the *only* way to recover the wallet. Show it to the user once,
571
+ * let them write it down, and never transmit or persist it in plaintext.
572
+ *
573
+ * ```ts
574
+ * const mnemonic = generateMnemonic() // 12 words
575
+ * const long = generateMnemonic({ words: 24 }) // 24 words
576
+ * ```
577
+ */
578
+ declare function generateMnemonic(options?: GenerateMnemonicOptions): string;
579
+ /**
580
+ * Check a recovery phrase for the right word count, known words, and a valid
581
+ * checksum. Use it to give immediate feedback on a restore form rather than
582
+ * waiting for {@link keypairFromMnemonic} to throw.
583
+ *
584
+ * Pure JavaScript — no engine needed.
585
+ */
586
+ declare function validateMnemonic(mnemonic: string): boolean;
587
+ /** Trim and collapse whitespace so pasted phrases validate as typed ones do. */
588
+ declare function normalizeMnemonic(mnemonic: string): string;
589
+
590
+ /** Identify the account either by its public key or by its account id. */
591
+ type AccountIdentifier = {
592
+ readonly publicKey: Uint8Array;
593
+ readonly accountId?: never;
594
+ } | {
595
+ readonly accountId: Uint8Array;
596
+ readonly publicKey?: never;
597
+ };
598
+ type CreateWalletAddressInput = AccountIdentifier & WalletAddressOptions;
599
+ /**
600
+ * Build the base58 wallet address for an account. Synchronous; requires the engine
601
+ * to be loaded already. Most callers want {@link createWalletAddress} instead.
602
+ */
603
+ declare function createWalletAddressSync(input: CreateWalletAddressInput): string;
604
+ /**
605
+ * Build the base58 wallet address for an account, loading the engine if needed.
606
+ *
607
+ * ```ts
608
+ * const address = await createWalletAddress({ publicKey, chainNumber: 1 })
609
+ * ```
610
+ */
611
+ declare function createWalletAddress(input: CreateWalletAddressInput): Promise<string>;
612
+ /** Decode a base58 wallet address. Synchronous; requires the engine to be loaded. */
613
+ declare function decodeWalletAddressSync(address: string): DecodedWalletAddress;
614
+ /** Decode a base58 wallet address, loading the engine if needed. */
615
+ declare function decodeWalletAddress(address: string): Promise<DecodedWalletAddress>;
616
+ /**
617
+ * True when `address` decodes as a Geeq wallet address. Requires a loaded engine.
618
+ *
619
+ * Throws `not-initialized` rather than answering before the engine has loaded: `false`
620
+ * would call every address invalid, including good ones.
621
+ */
622
+ declare function isValidWalletAddress(address: string): boolean;
623
+
624
+ /** Every record type this SDK understands, for validation and tooling. */
625
+ declare const GEEQ_RECORD_TYPES: readonly GeeqRecordType[];
626
+ declare const DEFAULT_RECORD_TYPE: GeeqRecordType;
627
+
628
+ /**
629
+ * An amount of coin, with its unit stated.
630
+ *
631
+ * The protocol counts only **pennies**, as geeq_node_core calls them. A Geeq is a display
632
+ * unit, and how many pennies make one is not on the chain: the app says, as
633
+ * `penniesPerGeeq` when it connects, and every conversion takes it from there.
634
+ *
635
+ * Every amount crossing this SDK's surface names its unit, because the alternative is a
636
+ * bare number that means one thing to the caller and another to the chain. That mistake
637
+ * does not throw — it just moves the wrong amount.
638
+ *
639
+ * ```ts
640
+ * { geeq: "12.34" } // human-facing: what someone typed
641
+ * { pennies: balance } // protocol-facing: as the chain counts it
642
+ * ```
643
+ */
644
+ type GeeqAmount = {
645
+ readonly geeq: string | number;
646
+ } | {
647
+ readonly pennies: bigint | number;
648
+ };
649
+ /**
650
+ * Resolve any {@link GeeqAmount} to pennies, the unit the protocol uses. A `{ geeq }`
651
+ * figure is read at `penniesPerGeeq` to the Geeq; `{ pennies }` is taken as it is.
652
+ */
653
+ declare function toPennies(amount: GeeqAmount, penniesPerGeeq: bigint): bigint;
654
+ /**
655
+ * Format pennies as a decimal Geeq string at `penniesPerGeeq` to the Geeq, the way the Geeq
656
+ * wallet shows amounts: every place the unit has, trailing zeros dropped.
657
+ *
658
+ * A negative figure — a shortfall, say — keeps its sign: `-1.5`, not `-1.-5`.
659
+ */
660
+ declare function formatGeeq(pennies: bigint, penniesPerGeeq: bigint): string;
661
+
662
+ /** A response, narrowed to what this SDK reads. A WHATWG `Response` satisfies it. */
663
+ interface GeeqHttpResponse {
664
+ readonly ok: boolean;
665
+ readonly status: number;
666
+ text(): Promise<string>;
667
+ }
668
+ /** A request, narrowed to what this SDK sends. Assignable to `RequestInit`. */
669
+ interface GeeqRequestInit {
670
+ readonly method?: string;
671
+ readonly headers?: Readonly<Record<string, string>>;
672
+ readonly body?: string;
673
+ readonly signal?: AbortSignal;
674
+ }
675
+ /**
676
+ * How the SDK reaches a node.
677
+ *
678
+ * Deliberately a structural subset of WHATWG `fetch`, so `{ transport: fetch }`
679
+ * typechecks with no adapter, and so does undici with a custom dispatcher, a relative
680
+ * URL through a dev proxy, or a scripted array of responses in a test.
681
+ */
682
+ type GeeqTransport = (url: string, init?: GeeqRequestInit) => Promise<GeeqHttpResponse>;
683
+ /**
684
+ * `globalThis.fetch`, resolved at call time.
685
+ *
686
+ * Never a stored reference: an unbound `fetch` throws `Illegal invocation` in a
687
+ * browser, and capturing it would defeat any test that swaps `globalThis.fetch` after
688
+ * this module is imported.
689
+ */
690
+ declare const defaultTransport: GeeqTransport;
691
+ interface RequestOptions {
692
+ readonly transport: GeeqTransport;
693
+ readonly timeoutMs: number;
694
+ readonly retries: number;
695
+ readonly headers?: Readonly<Record<string, string>> | undefined;
696
+ readonly signal?: AbortSignal | undefined;
697
+ readonly method?: string | undefined;
698
+ readonly body?: string | undefined;
699
+ }
700
+
701
+ /** How the directory classifies a node. Only `'active'` processes transactions. */
702
+ type GeeqNodeKind = 'active' | 'relay' | 'listener' | 'chain-admin' | 'none' | 'unknown';
703
+ /** One chain the directory knows about. */
704
+ interface GeeqDiscoveredChain {
705
+ /** The directory's own row id, which every other directory route calls `systemId`. */
706
+ readonly systemId: number;
707
+ /** The protocol chain number — what a transaction carries and a node checks. */
708
+ readonly chainNumber: number;
709
+ readonly name: string | undefined;
710
+ }
711
+ /**
712
+ * One node the directory knows about.
713
+ *
714
+ * Note what is **missing**: a node number. The directory can say where a node is but
715
+ * not who it is in the chain's own net actor list, so the ledger read is still the only
716
+ * way to learn the number a transaction must carry.
717
+ */
718
+ interface GeeqDiscoveredNode {
719
+ readonly host: string;
720
+ readonly kind: GeeqNodeKind;
721
+ /** The directory's own lifecycle state — `new`, `ready`, `stopped`. */
722
+ readonly status: string;
723
+ /** Where to reach it. Always the client port; the directory's own port is internal. */
724
+ readonly url: string;
725
+ }
726
+ /** Every chain the directory knows about. */
727
+ declare function listChains(origin: string, request: RequestOptions): Promise<GeeqDiscoveredChain[]>;
728
+ interface NodeAddressing {
729
+ /** Scheme for the node URLs. Defaults to the directory's own. */
730
+ readonly protocol?: string | undefined;
731
+ /** Client port. Defaults to {@link GEEQ_NODE_PORT}; override only for local setups. */
732
+ readonly port?: number | undefined;
733
+ }
734
+ /** Every node the directory lists for one chain. */
735
+ declare function listNodes(origin: string, systemId: number, request: RequestOptions, addressing?: NodeAddressing): Promise<GeeqDiscoveredNode[]>;
736
+ interface DiscoverOptions extends NodeAddressing {
737
+ readonly origin: string;
738
+ /** Select the chain by the name the directory gives it. Matched case-insensitively. */
739
+ readonly chainName?: string | undefined;
740
+ /** Select by protocol chain number, or — alongside `chainName` — assert it. */
741
+ readonly chainNumber?: number | undefined;
742
+ readonly request: RequestOptions;
743
+ }
744
+ interface Discovery {
745
+ readonly chain: GeeqDiscoveredChain;
746
+ /** Every node listed for the chain, in directory order. */
747
+ readonly nodes: readonly GeeqDiscoveredNode[];
748
+ /** Those that will actually take a transaction. */
749
+ readonly usable: readonly GeeqDiscoveredNode[];
750
+ }
751
+ /**
752
+ * Find a chain in the directory and list its nodes.
753
+ *
754
+ * This reads two routes and nothing else. The directory is a deployment control plane:
755
+ * it has no authentication, and its other routes create and destroy machines and serve
756
+ * private keys. Treat every value it returns as a hint about *where* to look, never as
757
+ * authority about the chain itself — that comes from the ledger.
758
+ */
759
+ declare function discover(options: DiscoverOptions): Promise<Discovery>;
760
+
761
+ /** What kind of actor a net actor record describes. */
762
+ type GeeqNodeRole = 'anchor-node' | 'anon-node' | 'cert-node' | 'cert-authority' | 'chain-admin' | 'relay' | 'treasury-agent' | 'unknown';
763
+ /** What that actor is currently doing. */
764
+ type GeeqNodeStatus = 'chain-admin' | 'hub' | 'leaving' | 'listening' | 'node' | 'ready' | 'relay' | 'suspended' | 'unknown';
765
+ /** One net actor record, as plain values. */
766
+ interface GeeqLedgerNode {
767
+ /** `nodeNumSelf` — what a transaction must name, checked with strict equality. */
768
+ readonly nodeNumber: number;
769
+ /** The bare IP the ledger records. Nodes are matched on this and nothing else. */
770
+ readonly host: string;
771
+ /**
772
+ * The port recorded against this node in the ledger.
773
+ *
774
+ * **Not the port you connect to.** Clients always reach a node on 2082, where TLS is
775
+ * terminated; every first-party client ignores this field and rebuilds the URL from
776
+ * its own configuration. Exposed for diagnostics, and used only to break a tie when
777
+ * one address carries several records.
778
+ */
779
+ readonly port: number;
780
+ readonly role: GeeqNodeRole;
781
+ readonly status: GeeqNodeStatus;
782
+ /**
783
+ * True when the protocol permits this node to accept transactions.
784
+ *
785
+ * **Not a liveness signal.** Nothing in the protocol evicts a crashed node from the
786
+ * net actor list, and `blockNumLiveness` is rewritten for every actor on every commit
787
+ * rather than per node, so it carries no health information at all. A node that died
788
+ * an hour ago still reads as processing here. The only way to learn a node is up is
789
+ * to talk to it.
790
+ */
791
+ readonly processing: boolean;
792
+ /**
793
+ * `blockNumRemove` — the block at which this node's turn in the active set ends.
794
+ *
795
+ * Reset 100–355 blocks ahead each time the node is promoted, so it is a rotation
796
+ * deadline rather than a departure flag.
797
+ */
798
+ readonly retiresAtBlock: number;
799
+ /**
800
+ * True when this node is inside its own shutdown window and will refuse work.
801
+ *
802
+ * The node's submit handler answers HTTP 503 `'Block chain is stopped'` once its
803
+ * `blockNumRemove` is less than its current block plus two, so this is knowable from
804
+ * the ledger before a transaction is ever built.
805
+ */
806
+ readonly retiring: boolean;
807
+ }
808
+
809
+ /** @internal — built by {@link decodeValidationLedger}, never by hand. */
810
+ interface GeeqLedgerInit {
811
+ readonly chainNumber: number;
812
+ readonly blockNumber: number;
813
+ readonly nodes: readonly GeeqLedgerNode[];
814
+ readonly metadataLimits: GeeqMetadataLimits | undefined;
815
+ readonly minimumTransfer: bigint | undefined;
816
+ readonly geeqNorm: bigint | undefined;
817
+ readonly accounts: ReadonlyMap<string, CoinAccountFacts>;
818
+ readonly proxies: ReadonlyMap<string, ProxyAccountFacts>;
819
+ readonly readAt: number;
820
+ readonly nodeUrl: string | undefined;
821
+ }
822
+ /**
823
+ * One validation layer ledger, decoded into plain values.
824
+ *
825
+ * This is a snapshot, not a view: the Cap'n Proto `Message` it came from is released
826
+ * the moment decoding finishes. That is deliberate and load-bearing. capnp-ts counts a
827
+ * traversal budget per `Message` and never resets it, so a retained handle rescanned on
828
+ * every transaction eventually throws `PTR_TRAVERSAL_LIMIT_EXCEEDED`; and reading a
829
+ * malformed union arm mutates the message in place. Neither can happen to a snapshot.
830
+ *
831
+ * It holds no WebAssembly handle, so unlike a keypair it needs no `dispose()`.
832
+ */
833
+ declare class GeeqLedger {
834
+ /** The ledger's own chain number. A transaction must name exactly this. */
835
+ readonly chainNumber: number;
836
+ /** N — the last block committed to this ledger. */
837
+ readonly blockNumber: number;
838
+ /** N + 1 — the block a transaction built now should ask for. */
839
+ readonly targetBlockNumber: number;
840
+ /** Every net actor, in ledger order. */
841
+ readonly nodes: readonly GeeqLedgerNode[];
842
+ /** Undefined when this chain publishes no metadata parameters. */
843
+ readonly metadataLimits: GeeqMetadataLimits | undefined;
844
+ /**
845
+ * The smallest transfer this chain accepts, in pennies.
846
+ *
847
+ * Undefined when the chain does not publish the parameters it is derived from.
848
+ */
849
+ readonly minimumTransfer: bigint | undefined;
850
+ /**
851
+ * The chain's `geeqNorm` parameter, which the minimum transfer and every fee are
852
+ * multiples of. Undefined when the chain does not publish it.
853
+ */
854
+ readonly geeqNorm: bigint | undefined;
855
+ /** `Date.now()` when this was read, so callers can judge staleness. */
856
+ readonly readAt: number;
857
+ /** The node it was read from, when it came from one. */
858
+ readonly nodeUrl: string | undefined;
859
+ private readonly accounts;
860
+ private readonly proxies;
861
+ /** @internal */
862
+ constructor(init: GeeqLedgerInit);
863
+ /** The nodes that will actually accept a transaction. */
864
+ get processingNodes(): readonly GeeqLedgerNode[];
865
+ /**
866
+ * Find a net actor record.
867
+ *
868
+ * By `host` this matches the bare IP and nothing else — every first-party client does
869
+ * the same, because a record's `port` is the node's internal listen port while clients
870
+ * connect through a proxy on another one. Matching on both finds nothing.
871
+ */
872
+ findNode(match: {
873
+ readonly host: string;
874
+ } | {
875
+ readonly nodeNumber: number;
876
+ }): GeeqLedgerNode | undefined;
877
+ /** Every net actor record at one host. More than one is possible, and ambiguous. */
878
+ findNodes(host: string): readonly GeeqLedgerNode[];
879
+ /**
880
+ * The signing account's coin record, or `undefined` when this chain has none for it.
881
+ *
882
+ * Absence is the ordinary state of a freshly generated keypair: an account appears in
883
+ * the ledger only once it has been funded.
884
+ */
885
+ findCoinAccount(account: AccountRef): GeeqCoinAccount | undefined;
886
+ /** A proxy record, or `undefined` when this chain has none with that id. */
887
+ findProxyAccount(account: AccountRef): GeeqProxyAccount | undefined;
888
+ /**
889
+ * Every NFT mint proxy an account administers — the ones it can mint through.
890
+ *
891
+ * Found by the admin each record names, so it needs nothing remembered from when they
892
+ * were created.
893
+ */
894
+ findProxyAccounts(match: {
895
+ readonly admin: AccountRef;
896
+ }): readonly GeeqProxyAccount[];
897
+ /** How many coin accounts this ledger carries. Mostly useful for diagnostics. */
898
+ get coinAccountCount(): number;
899
+ /** Summary only — the account table is far too large to serialise. */
900
+ toJSON(): Record<string, unknown>;
901
+ toString(): string;
902
+ }
903
+
904
+ /** The protocol attests a 32-byte BLAKE3 digest and rejects any other length. */
905
+ declare const ATTEST_HASH_BYTES = 32;
906
+ /**
907
+ * Anything that can be attested.
908
+ *
909
+ * A `File` from an `<input type="file">` is a `Blob`, so it can be passed straight in —
910
+ * there is no need to read it into a buffer first. A string is encoded as UTF-8.
911
+ */
912
+ type GeeqContent = Uint8Array | ArrayBuffer | Blob | string;
913
+ /** Read any accepted content into bytes. Async only because a `Blob` requires it. */
914
+ declare function readContent(content: GeeqContent): Promise<Uint8Array>;
915
+ /**
916
+ * The digest that goes on chain.
917
+ *
918
+ * The content itself never leaves the device; only this does. Pure BLAKE3, so it needs
919
+ * no engine and no network and works before {@link initGeeq} has resolved — which is
920
+ * what lets a UI show the digest as someone types.
921
+ *
922
+ * Use this rather than calling a hash library directly: the length and the algorithm are
923
+ * both fixed by the protocol, and a digest of any other width is rejected when the
924
+ * transaction is built.
925
+ */
926
+ declare function attestHashOfSync(content: Uint8Array | ArrayBuffer | string): Uint8Array;
927
+ /** {@link attestHashOfSync}, also accepting a `Blob` or a `File`. */
928
+ declare function attestHashOf(content: GeeqContent): Promise<Uint8Array>;
929
+ /** Reject a digest the chain would refuse, naming what was wrong. */
930
+ declare function assertAttestHash(attestHash: unknown): Uint8Array;
931
+
932
+ /** @internal */
933
+ interface NodeRequestConfig {
934
+ readonly transport: GeeqTransport;
935
+ readonly timeoutMs: number;
936
+ readonly retries: number;
937
+ readonly headers: Readonly<Record<string, string>> | undefined;
938
+ }
939
+ /** @internal */
940
+ interface GeeqNodeInit extends NodeRequestConfig {
941
+ readonly url: string;
942
+ readonly nodeNumber: number;
943
+ readonly host: string;
944
+ readonly port: number;
945
+ readonly role: GeeqNodeRole;
946
+ readonly status: GeeqNodeStatus;
947
+ readonly processing: boolean;
948
+ }
949
+ /**
950
+ * One node, identified in its own chain's ledger.
951
+ *
952
+ * A node is only meaningful once a ledger has confirmed which actor it is: the number a
953
+ * transaction must carry is the node's own `nodeNumSelf`, checked with strict equality,
954
+ * and nothing outside the ledger reports it.
955
+ */
956
+ declare class GeeqNode {
957
+ /** Absolute origin, no trailing slash. */
958
+ readonly url: string;
959
+ /** `nodeNumSelf` — what a transaction targeting this node must name. */
960
+ readonly nodeNumber: number;
961
+ /** The bare IP the ledger records for it. */
962
+ readonly host: string;
963
+ /** Its own listen port from the ledger, which is not necessarily {@link url}'s. */
964
+ readonly port: number;
965
+ readonly role: GeeqNodeRole;
966
+ readonly status: GeeqNodeStatus;
967
+ /** Whether it accepts transactions at all. */
968
+ readonly processing: boolean;
969
+ private readonly request;
970
+ /** @internal */
971
+ constructor(init: GeeqNodeInit);
972
+ /**
973
+ * Where a transaction built for this node must be POSTed.
974
+ *
975
+ * The SDK does not submit. This exists so an application posts to the node the SDK
976
+ * actually read from, instead of keeping a second copy of the URL that can drift.
977
+ */
978
+ get submitUrl(): string;
979
+ /** Read this node's current ledger. No caching — {@link GeeqChain} owns that. */
980
+ readLedger(options?: {
981
+ readonly signal?: AbortSignal;
982
+ }): Promise<GeeqLedger>;
983
+ toJSON(): Record<string, unknown>;
984
+ toString(): string;
985
+ }
986
+
987
+ interface GeeqChainOptions {
988
+ /**
989
+ * How many pennies make one Geeq: a power of ten, as a bigint.
990
+ *
991
+ * The chain counts only pennies and does not publish this, so the app says — use the
992
+ * divisor its wallet and explorer show amounts with. {@link GeeqChain.toPennies} reads
993
+ * `{ geeq }` figures with it, and {@link GeeqChain.formatGeeq} and the chain's own
994
+ * messages show amounts with it.
995
+ */
996
+ readonly penniesPerGeeq: bigint;
997
+ /**
998
+ * The chain's node directory — the simulator URL for your environment — which the SDK
999
+ * asks for an active node. Works for every chain and environment.
1000
+ *
1001
+ * Supply this or {@link endpoint}, not both. The directory is the only thing that
1002
+ * knows which nodes exist and which are running; the ledger then says which actor the
1003
+ * chosen one is. Note the directory is a deployment control plane with no
1004
+ * authentication — the SDK reads two of its routes and never touches the rest.
1005
+ */
1006
+ readonly discovery?: string | URL;
1007
+ /**
1008
+ * A specific node, when you already know one.
1009
+ *
1010
+ * Supply this or {@link discovery}, not both.
1011
+ */
1012
+ readonly endpoint?: string | URL;
1013
+ /**
1014
+ * Which chain to use, by the name its directory gives it.
1015
+ *
1016
+ * Only meaningful with {@link discovery} — a name exists nowhere but the directory.
1017
+ * Matched case-insensitively.
1018
+ */
1019
+ readonly chainName?: string;
1020
+ /**
1021
+ * Which chain to use, by protocol number.
1022
+ *
1023
+ * With {@link discovery} this selects the chain when no {@link chainName} is given,
1024
+ * and asserts it when one is. With {@link endpoint} it asserts the node serves the
1025
+ * chain you expect, and a mismatch throws `chain-mismatch` on the first ledger read.
1026
+ */
1027
+ readonly chainNumber?: number;
1028
+ /** Defaults to a call through `globalThis.fetch`. */
1029
+ readonly transport?: GeeqTransport;
1030
+ /** Per request. Defaults to 10000. */
1031
+ readonly timeoutMs?: number;
1032
+ /** Extra headers on every request — a proxy token, a trace id. */
1033
+ readonly headers?: Readonly<Record<string, string>>;
1034
+ /** How long a read ledger may be reused. Defaults to 5000. 0 disables the cache. */
1035
+ readonly ledgerMaxAgeMs?: number;
1036
+ /** Skip host matching. Required behind a gateway or when using a DNS name. */
1037
+ readonly nodeNumber?: number;
1038
+ /**
1039
+ * The port discovered nodes are reached on. Defaults to 2082.
1040
+ *
1041
+ * Deployment configuration, not node selection — override it only when running
1042
+ * against nodes that are not behind the standard TLS front door, such as a local
1043
+ * test harness.
1044
+ */
1045
+ readonly nodePort?: number;
1046
+ /** Retries on transport failure only, never on a response. Defaults to 1. */
1047
+ readonly retries?: number;
1048
+ readonly signal?: AbortSignal;
1049
+ }
1050
+ interface AttestationRequest {
1051
+ /** The account that signs. Must be a coin-asset account on this chain. */
1052
+ readonly keypair: GeeqKeypair;
1053
+ /**
1054
+ * What to attest. Hashed here with BLAKE3-32; the content never leaves the device.
1055
+ *
1056
+ * A `File` straight from a file input works, as does a string or raw bytes.
1057
+ */
1058
+ readonly content?: GeeqContent;
1059
+ /** Or supply the digest yourself. Exactly one of `content` and `attestHash`. */
1060
+ readonly attestHash?: Uint8Array;
1061
+ /** Public labels, checked against this chain's limits before anything is signed. */
1062
+ readonly metadata?: readonly (string | Uint8Array)[];
1063
+ /** Override the derived nonce. See {@link NonceGuard} for when that is needed. */
1064
+ readonly nonce?: number;
1065
+ readonly signal?: AbortSignal;
1066
+ }
1067
+ interface CoinTransferRequest {
1068
+ /** The account paying. Must be a coin-asset account on this chain. */
1069
+ readonly from: GeeqKeypair;
1070
+ /**
1071
+ * Who receives it.
1072
+ *
1073
+ * A base58 wallet address is the usual thing — it is what a person pastes — and the
1074
+ * chain it encodes is checked against this one. A raw account id or public key works
1075
+ * too, but carries no chain to check.
1076
+ */
1077
+ readonly to: string | AccountRef;
1078
+ /**
1079
+ * How much. State the unit: `{ geeq: "1.5" }`, read at {@link GeeqChainOptions.penniesPerGeeq},
1080
+ * or `{ pennies }` as the chain counts them. May be left out
1081
+ * when sending NFTs, which then go with the chain's minimum transfer.
1082
+ */
1083
+ readonly amount?: GeeqAmount;
1084
+ /** NFTs to send, by id — see {@link GeeqChain.findNfts}. The sender must hold each one. */
1085
+ readonly nfts?: readonly Uint8Array[];
1086
+ /** Override the derived nonce. See {@link NonceGuard}. */
1087
+ readonly nonce?: number;
1088
+ readonly signal?: AbortSignal;
1089
+ }
1090
+ interface ChainStateRequest {
1091
+ readonly account: AccountRef;
1092
+ readonly maxAgeMs?: number;
1093
+ readonly signal?: AbortSignal;
1094
+ }
1095
+ interface NftMintProxyRequest {
1096
+ /**
1097
+ * The account paying for the proxy, which also becomes its admin: the only key that
1098
+ * can later mint NFTs through it. Must be a coin-asset account on this chain.
1099
+ */
1100
+ readonly admin: GeeqKeypair;
1101
+ /**
1102
+ * Coin moved onto the proxy, `{ geeq }` or `{ pennies }` like any amount. It pays for the
1103
+ * mints made through it, so fund it for more than one.
1104
+ */
1105
+ readonly amount: GeeqAmount;
1106
+ /**
1107
+ * Which of the admin's proxies this is, from 1. Leave it out to take the next one —
1108
+ * one past the highest the ledger holds for this admin, see
1109
+ * {@link GeeqChain.findNftMintProxies}. An index already in use is refused.
1110
+ */
1111
+ readonly index?: number;
1112
+ /** Override the derived nonce. See {@link NonceGuard}. */
1113
+ readonly nonce?: number;
1114
+ readonly signal?: AbortSignal;
1115
+ }
1116
+ interface NftMintProxiesRequest {
1117
+ /**
1118
+ * The admin whose proxies to find. A keypair will do; so will a bare public key, since
1119
+ * listing needs no signature. Not an account id: an index is derived from the key.
1120
+ */
1121
+ readonly admin: {
1122
+ readonly publicKey: Uint8Array;
1123
+ };
1124
+ /** How old a cached ledger may be. 0 reads afresh — a new proxy shows only once confirmed. */
1125
+ readonly maxAgeMs?: number;
1126
+ readonly signal?: AbortSignal;
1127
+ }
1128
+ interface NftsRequest {
1129
+ /** Whose NFTs. A keypair will do, as will an account id or a public key. */
1130
+ readonly owner: AccountRef;
1131
+ /** How old a cached ledger may be. 0 reads afresh — a new NFT shows only once confirmed. */
1132
+ readonly maxAgeMs?: number;
1133
+ readonly signal?: AbortSignal;
1134
+ }
1135
+ interface NftMintRequest {
1136
+ /** The proxy's admin: the only key that can mint through it. It signs the mint. */
1137
+ readonly admin: GeeqKeypair;
1138
+ /**
1139
+ * Which of the admin's proxies mints it, by index — see
1140
+ * {@link GeeqChain.findNftMintProxies}. The proxy pays the fee and {@link amount}, and
1141
+ * spends its own nonce, not the admin's.
1142
+ */
1143
+ readonly index: number;
1144
+ /**
1145
+ * What the NFT stands for. Hashed here with BLAKE3-32; the content never leaves the
1146
+ * device. A `File` straight from a file input works, as does a string or raw bytes.
1147
+ */
1148
+ readonly content?: GeeqContent;
1149
+ /** Or supply the digest yourself. Exactly one of `content` and `attestHash`. */
1150
+ readonly attestHash?: Uint8Array;
1151
+ /** Public labels stored with the NFT, checked against this chain's limits before signing. */
1152
+ readonly metadata?: readonly (string | Uint8Array)[];
1153
+ /** Who receives the NFT. Defaults to the admin. An address is checked against this chain. */
1154
+ readonly to?: string | AccountRef;
1155
+ /** Coin the proxy sends to the receiver with it. Defaults to the chain's minimum transfer. */
1156
+ readonly amount?: GeeqAmount;
1157
+ /** Override the proxy's derived nonce. See {@link NonceGuard}. */
1158
+ readonly nonce?: number;
1159
+ readonly signal?: AbortSignal;
1160
+ }
1161
+ /**
1162
+ * A chain, reached through one node.
1163
+ *
1164
+ * Holds the node directory, the metadata limits and a short-lived ledger cache, so an
1165
+ * attestation needs only a keypair and something to attest. Everything a transaction is
1166
+ * pinned to — chain number, target block, node number, nonce — is read from the ledger
1167
+ * rather than asked of the caller, because none of it can be known offline and a wrong
1168
+ * value is rejected by the chain.
1169
+ *
1170
+ * A chain is bound to the endpoint it was connected to. To use a different node, connect
1171
+ * another one: a different node means a different address, certificate and CORS origin,
1172
+ * which is not something to switch silently.
1173
+ *
1174
+ * In a reactive framework, mark an instance raw (Vue's `markRaw`) rather than letting it
1175
+ * be proxied.
1176
+ */
1177
+ declare class GeeqChain {
1178
+ /** The node this chain reads from and builds transactions for. */
1179
+ readonly endpoint: string;
1180
+ /** What the directory said about the nodes on this chain, when one was used. */
1181
+ readonly candidates: readonly GeeqDiscoveredNode[];
1182
+ /** The chain's name, when a directory supplied one. Display only — chains are numbered. */
1183
+ readonly chainName: string | undefined;
1184
+ /** How many pennies make one Geeq, as given to {@link GeeqChain.connect}. */
1185
+ readonly penniesPerGeeq: bigint;
1186
+ private readonly request;
1187
+ private readonly ledgerMaxAgeMs;
1188
+ private readonly nonces;
1189
+ private readonly declaredChainNumber;
1190
+ private readonly nodeNumberOverride;
1191
+ private cached;
1192
+ private resolved;
1193
+ private inflight;
1194
+ /** @internal */
1195
+ constructor(init: {
1196
+ endpoint: string;
1197
+ candidates: readonly GeeqDiscoveredNode[];
1198
+ chainName: string | undefined;
1199
+ chainNumber: number | undefined;
1200
+ nodeNumber: number | undefined;
1201
+ penniesPerGeeq: bigint;
1202
+ request: NodeRequestConfig;
1203
+ ledgerMaxAgeMs: number;
1204
+ });
1205
+ /**
1206
+ * Choose a chain and a node, without reading anything from the chain itself.
1207
+ *
1208
+ * With `discovery`, the directory is asked which nodes exist and one of the active
1209
+ * ones is taken. With `endpoint`, that node is used as given. Either way **no ledger
1210
+ * is fetched here** — a ledger read is only valid for a few blocks, so it happens when
1211
+ * a transaction is actually being built rather than when the app starts.
1212
+ */
1213
+ static connect(options: GeeqChainOptions): Promise<GeeqChain>;
1214
+ /** {@link GeeqChain.connect}, and install the result as the process-wide default. */
1215
+ static init(options: GeeqChainOptions): Promise<GeeqChain>;
1216
+ /** The default chain. Throws `not-initialized` when there is none. */
1217
+ static current(): GeeqChain;
1218
+ /** Whether a default chain has been installed. */
1219
+ static get initialized(): boolean;
1220
+ /** Clear the default. Call this from `afterEach` so tests do not leak into each other. */
1221
+ static reset(): void;
1222
+ /** Resolve an amount to pennies, reading a `{ geeq }` figure at {@link penniesPerGeeq}. */
1223
+ toPennies(amount: GeeqAmount): bigint;
1224
+ /** Show pennies as a Geeq figure at {@link penniesPerGeeq}, as the Geeq wallet does. */
1225
+ formatGeeq(pennies: bigint): string;
1226
+ /**
1227
+ * This chain's number.
1228
+ *
1229
+ * Known immediately when a directory named the chain, or once a ledger has been read.
1230
+ * `undefined` in between — with a bare endpoint and no declared number, only the node
1231
+ * can say.
1232
+ */
1233
+ get chainNumber(): number | undefined;
1234
+ /**
1235
+ * The node, once a ledger read has identified it in the net actor list.
1236
+ *
1237
+ * `undefined` until then: a node's number is the one thing the directory cannot say,
1238
+ * so it is not known before the first read.
1239
+ */
1240
+ get node(): GeeqNode | undefined;
1241
+ /** The net actor directory from the last ledger read, if there has been one. */
1242
+ get nodes(): readonly GeeqLedgerNode[];
1243
+ /** Those of them that accept transactions. */
1244
+ get processingNodes(): readonly GeeqLedgerNode[];
1245
+ /** The ledger from the last read, if it is still cached. */
1246
+ get ledger(): GeeqLedger | undefined;
1247
+ /**
1248
+ * A URL for another node in this chain.
1249
+ *
1250
+ * Every node answers client traffic on {@link GEEQ_NODE_PORT}, so only the scheme is
1251
+ * carried over from the endpoint in use. The port recorded against a node in the
1252
+ * ledger is its own internal listener and is never what you connect to.
1253
+ */
1254
+ endpointFor(host: string): string;
1255
+ /**
1256
+ * The current ledger, from cache when it is fresh enough.
1257
+ *
1258
+ * Staleness is safe in exactly one direction. The node accepts a target block within
1259
+ * `[N-8, N+1]` of its own head, and a cached ledger can only be *behind*, so the
1260
+ * target it yields is never in the future — the only rejected direction. The default
1261
+ * five seconds is set by the node list and metadata limits, which have no such
1262
+ * tolerance, not by the block window.
1263
+ */
1264
+ getLedger(options?: {
1265
+ readonly maxAgeMs?: number;
1266
+ readonly signal?: AbortSignal;
1267
+ }): Promise<GeeqLedger>;
1268
+ /** Discard the cached ledger and read again. */
1269
+ refresh(options?: {
1270
+ readonly signal?: AbortSignal;
1271
+ }): Promise<GeeqLedger>;
1272
+ /**
1273
+ * One read at a time.
1274
+ *
1275
+ * Same single-flight shape as `initGeeq`, including clearing the promise on failure so
1276
+ * a later call can retry rather than being stuck with the first error. Without it, a
1277
+ * page that builds several transactions at once pulls the whole ledger several times
1278
+ * from an endpoint with no rate limiting of any kind.
1279
+ *
1280
+ * The read is shared, so it carries no caller's signal: one caller aborting must not
1281
+ * fail the others waiting on it. Each caller stops waiting on its own signal instead,
1282
+ * and the read runs on, bounded by `timeoutMs`, to fill the cache for whoever is next.
1283
+ */
1284
+ private read;
1285
+ /**
1286
+ * Everything a transaction must be pinned to, read from this chain.
1287
+ *
1288
+ * Throws rather than guessing when the account cannot sign: absent from the ledger
1289
+ * (the ordinary state of an unfunded keypair), or present as a record type the
1290
+ * protocol will not accept.
1291
+ */
1292
+ getChainState(request: ChainStateRequest): Promise<GeeqChainState>;
1293
+ /**
1294
+ * What every transaction built from `ledger` is pinned to, bar the signer's nonce: the
1295
+ * same for a signer's own transaction and for a proxy's.
1296
+ */
1297
+ private pinnedTo;
1298
+ /** {@link getChainState} against a ledger already in hand, so a build reads only one. */
1299
+ private stateIn;
1300
+ /**
1301
+ * The ledger a build is made from, and the state of `signer` in it.
1302
+ *
1303
+ * A cached ledger is up to a few seconds old, so "this account already built against
1304
+ * this block" may only mean "we are looking at an old block". Look again once before
1305
+ * letting the nonce guard refuse — otherwise a caller who waited out the block interval
1306
+ * is told to wait for a block that has already passed. An explicit nonce is the caller
1307
+ * taking responsibility, so it skips the check.
1308
+ *
1309
+ * Everything the build then needs — what the sender holds, which proxies exist — comes
1310
+ * from this same ledger, so it all describes one block.
1311
+ */
1312
+ private readForBuild;
1313
+ /** Read afresh if `guardKey` already built against `ledger`'s block. See {@link readForBuild}. */
1314
+ private recheckIfClaimed;
1315
+ /**
1316
+ * Build and sign an attestation, deriving everything the chain decides.
1317
+ *
1318
+ * This is where the ledger is read, because this is the first moment the values it
1319
+ * carries are actually needed — and they go stale within a handful of blocks, so
1320
+ * reading them any earlier would only mean reading them twice.
1321
+ *
1322
+ * The result is **not submitted**. Hand it to {@link GeeqSocket.track} first, so its
1323
+ * verdict has somewhere to arrive, then POST `transaction` and `payload` to
1324
+ * {@link AttestationTx.nodeUrl}; any other node rejects it, because the node number is
1325
+ * checked with strict equality.
1326
+ */
1327
+ createAttestation(input: AttestationRequest): Promise<AttestationTx>;
1328
+ /**
1329
+ * Record the nonce a finished build took, so a second build against the same block is
1330
+ * refused rather than sent to share it.
1331
+ *
1332
+ * Claimed only once a transaction really exists, so a build that failed validation does
1333
+ * not consume the account's one nonce for this block. An explicit nonce is the caller
1334
+ * taking responsibility, so it bypasses the guard entirely.
1335
+ */
1336
+ private claimNonce;
1337
+ /**
1338
+ * Resolve a recipient, checking the chain it belongs to when one is stated.
1339
+ *
1340
+ * An address carries a chain number, and an address from another chain is almost
1341
+ * always a paste from the wrong place. The account id inside it would still be
1342
+ * technically usable here, which is exactly why this is worth refusing rather than
1343
+ * quietly sending.
1344
+ */
1345
+ private resolveRecipient;
1346
+ /**
1347
+ * Send coin to another account, and NFTs with it if you name them.
1348
+ *
1349
+ * Reads the ledger, so it also knows what the sender holds and what the chain's minimum
1350
+ * transfer is. The amount is checked against the minimum, and the sender's NFTs against
1351
+ * the ones named, before anything is signed; the balance against the amount plus the
1352
+ * fee once the signed transaction gives the fee. NFTs always travel with coin: leave
1353
+ * `amount` out and they go with the chain's minimum transfer.
1354
+ *
1355
+ * The result is **not submitted**. Hand it to {@link GeeqSocket.track} first, then POST
1356
+ * `transaction` to {@link CoinTransferTx.nodeUrl} using `coinTransferSubmitBody`, which
1357
+ * sends it as type `uvt` — a different shape from an attestation, and with no separate
1358
+ * payload.
1359
+ */
1360
+ createTransfer(input: CoinTransferRequest): Promise<CoinTransferTx>;
1361
+ /**
1362
+ * The NFTs an account holds on this chain, in the order its record lists them, each with
1363
+ * the hash and labels it was minted with.
1364
+ *
1365
+ * Which NFTs it holds comes from the validation layer ledger; what each stands for, from
1366
+ * the application layer ledger, which is read afresh every time. An account the chain
1367
+ * does not know holds none. A new NFT shows once a block has confirmed it, so pass
1368
+ * `maxAgeMs: 0` to look straight after one was minted or sent.
1369
+ */
1370
+ findNfts(request: NftsRequest): Promise<readonly GeeqNft[]>;
1371
+ /**
1372
+ * The NFT mint proxies an admin has on this chain, each with its index, and the index
1373
+ * its next proxy takes: one past the highest in use, or 1 for an admin with none.
1374
+ *
1375
+ * A proxy appears once a block has confirmed it, so pass `maxAgeMs: 0` to look
1376
+ * straight after one was submitted.
1377
+ */
1378
+ findNftMintProxies(request: NftMintProxiesRequest): Promise<GeeqNftMintProxies>;
1379
+ /**
1380
+ * Create a proxy to mint NFTs through.
1381
+ *
1382
+ * A proxy is a record of its own: it holds coin, carries its own nonce, and names one
1383
+ * admin — `admin` here — who signs every mint made through it. Its id is derived from
1384
+ * the admin's key and an index, as the Geeq wallet derives it: by default the next
1385
+ * index {@link findNftMintProxies} reports, so it is found again from the key alone.
1386
+ *
1387
+ * Checks the funding against the chain's minimum, and the admin's balance against the
1388
+ * funding plus the fee. The result is **not submitted** — track it, then post it with
1389
+ * `coinTransferSubmitBody`, exactly as a transfer. The proxy exists once a block
1390
+ * confirms it.
1391
+ */
1392
+ createNftMintProxy(input: NftMintProxyRequest): Promise<NftMintProxyTx>;
1393
+ /**
1394
+ * Mint an NFT through one of the admin's proxies.
1395
+ *
1396
+ * The proxy is the sender: it pays the fee and the coin sent with the NFT, and spends
1397
+ * its own nonce, not the admin's. The admin signs. The NFT itself is the content's hash
1398
+ * and labels, as an attestation is, and its id is derived from them — so the same
1399
+ * content with the same labels through the same proxy cannot be minted twice.
1400
+ *
1401
+ * Reads the ledger to find the proxy and check what it holds. The result is **not
1402
+ * submitted** — track it, then post it with `attestationSubmitBody`, exactly as an
1403
+ * attestation: two fields, as `uat`.
1404
+ */
1405
+ mintNft(input: NftMintRequest): Promise<NftMintTx>;
1406
+ /**
1407
+ * Whether a built transaction is now too old to send: the chain is past its
1408
+ * `validUntilBlock`, and a node would refuse it with an HTTP 400.
1409
+ *
1410
+ * Reads the ledger afresh, because a cached one can only be behind the chain and would
1411
+ * say "not yet" when it already is. Call it before submitting a transaction that may
1412
+ * have waited.
1413
+ */
1414
+ isExpired(tx: {
1415
+ readonly validUntilBlock: number;
1416
+ }): Promise<boolean>;
1417
+ /**
1418
+ * Give back the nonce a built transaction took, because it will never be sent — the
1419
+ * user changed something and is building again. The next build may then take the same
1420
+ * nonce in the same block instead of being refused with `nonce-collision`.
1421
+ *
1422
+ * Only for a transaction that was not submitted: if it reaches the node as well as its
1423
+ * rebuild, the two share a nonce and the node rejects both.
1424
+ */
1425
+ discard(tx: {
1426
+ readonly transactionHash: Uint8Array;
1427
+ }): void;
1428
+ /** Forget which accounts have built a transaction against the current block. */
1429
+ forgetNonces(): void;
1430
+ /** Drop cached state, and step down as the default if this chain holds that slot. */
1431
+ dispose(): void;
1432
+ toJSON(): Record<string, unknown>;
1433
+ toString(): string;
1434
+ }
1435
+ /**
1436
+ * Connect to a chain and make it the default.
1437
+ *
1438
+ * Thin alias for {@link GeeqChain.init} — the name most applications reach for, and one
1439
+ * line so there is no second implementation to keep in step.
1440
+ *
1441
+ * ```ts
1442
+ * await initChain({ endpoint: 'https://1.2.3.4:2082' })
1443
+ * const tx = await attest({ keypair, content: fileBytes })
1444
+ * ```
1445
+ */
1446
+ declare function initChain(options: GeeqChainOptions): Promise<GeeqChain>;
1447
+ /** Build an attestation on the default chain. See {@link GeeqChain.createAttestation}. */
1448
+ declare function attest(input: AttestationRequest): Promise<AttestationTx>;
1449
+ /** Send coin on the default chain. See {@link GeeqChain.createTransfer}. */
1450
+ declare function transfer(input: CoinTransferRequest): Promise<CoinTransferTx>;
1451
+
1452
+ /**
1453
+ * The port every Geeq node answers client traffic on.
1454
+ *
1455
+ * Nodes terminate TLS on 2082 and that is the only client-facing port; an endpoint
1456
+ * given without one is resolved to it rather than to the scheme's default, because a
1457
+ * request to 443 would fail opaquely in a browser with no way to tell why.
1458
+ */
1459
+ declare const GEEQ_NODE_PORT = 2082;
1460
+ interface SelectNodeOptions {
1461
+ /** Skip host matching entirely. Required behind a gateway or a DNS name. */
1462
+ readonly nodeNumber?: number | undefined;
1463
+ }
1464
+ /**
1465
+ * Choose the net actor record that a transaction should name.
1466
+ *
1467
+ * Matching is on **host only**. A record's `port` is the node's own listen port, while
1468
+ * a client reaches it through a proxy on another one, so requiring both to agree finds
1469
+ * nothing on a correctly deployed chain. Port is used solely to disambiguate when one
1470
+ * address carries several records.
1471
+ *
1472
+ * This never silently retargets. A different node means a different address, a
1473
+ * different certificate, a different CORS origin and a different operator seeing the
1474
+ * transaction, so an unusable node is reported with its alternatives and the caller
1475
+ * decides.
1476
+ */
1477
+ declare function selectNode(ledger: GeeqLedger, endpoint: string | URL, options?: SelectNodeOptions): GeeqLedgerNode;
1478
+
1479
+ /**
1480
+ * Every node route the SDK knows, in one place.
1481
+ *
1482
+ * `origin` is an absolute node URL with no trailing slash — see `originOf`.
1483
+ */
1484
+ /** `GET` the current validation layer ledger, base64 packed capnp under `result`. */
1485
+ declare function ledgerRoute(origin: string): string;
1486
+ /**
1487
+ * Where a signed transaction is POSTed.
1488
+ *
1489
+ * The SDK does not submit — this is exported so an application builds the same URL the
1490
+ * SDK read from, rather than keeping its own copy of the path.
1491
+ */
1492
+ declare function submitRoute(origin: string): string;
1493
+ /**
1494
+ * The node's client WebSocket, where transaction status events arrive.
1495
+ *
1496
+ * Same host and port as the HTTP API — the node's TLS front door routes `/ws/` to the
1497
+ * socket server and `/api/` to the HTTP one — so the only change is the scheme. The
1498
+ * trailing slash is part of the route: the proxy answers `/ws` with a redirect, and a
1499
+ * WebSocket handshake treats a redirect as failure.
1500
+ */
1501
+ declare function socketRoute(origin: string): string;
1502
+ /**
1503
+ * The body a node expects for an application-layer transaction: an attestation or an NFT
1504
+ * mint, each a transaction and its payload.
1505
+ */
1506
+ declare function attestationSubmitBody(transaction: string, payload: string): string;
1507
+ /**
1508
+ * The body a node expects for a coin transfer.
1509
+ *
1510
+ * A different type and a different shape from an attestation: a validation-layer
1511
+ * transaction carries no separate payload, so there is one blob rather than two.
1512
+ */
1513
+ declare function coinTransferSubmitBody(transaction: string): string;
1514
+
1515
+ /** A transaction as a block explorer reports it, field for field. */
1516
+ interface GeeqExplorerTransaction {
1517
+ /** Hex — the form the explorer files transactions under. */
1518
+ readonly refHash: string;
1519
+ /** The transaction's kind, such as `coin`. */
1520
+ readonly type: string;
1521
+ /** The explorer's verdict on it. */
1522
+ readonly status: string;
1523
+ readonly block: number;
1524
+ readonly chainName: string;
1525
+ readonly chainNumber: number;
1526
+ /** The sender's wallet address. */
1527
+ readonly from: string;
1528
+ readonly senderType: string;
1529
+ /** The receiver's wallet address, or empty when it has none. */
1530
+ readonly to: string;
1531
+ readonly receiverType: string;
1532
+ readonly publicKey: string;
1533
+ readonly signature: string;
1534
+ readonly createdAt: string;
1535
+ readonly fee: string;
1536
+ readonly acctNumApp?: string;
1537
+ /** What it moved — coin, tokens, NFTs — with amounts as decimal strings. */
1538
+ readonly values: readonly {
1539
+ readonly type: string;
1540
+ readonly value: string;
1541
+ readonly name?: string;
1542
+ }[];
1543
+ /** An attestation's hash and metadata. */
1544
+ readonly payload?: {
1545
+ readonly attestationHash: string;
1546
+ readonly metadata: readonly string[];
1547
+ };
1548
+ }
1549
+ interface ExplorerTransactionRequest {
1550
+ /** The explorer's base URL, such as `https://blkexp.geeq-test.io`. */
1551
+ readonly explorerUrl: string | URL;
1552
+ /**
1553
+ * The transaction's refHash in base64 — what `transactionIdOf(tx)` returns and socket
1554
+ * status events carry. It is sent to the explorer as hex.
1555
+ */
1556
+ readonly refHash: string;
1557
+ /** Defaults to `globalThis.fetch`. */
1558
+ readonly transport?: GeeqTransport;
1559
+ /** Defaults to 10000. */
1560
+ readonly timeoutMs?: number;
1561
+ readonly signal?: AbortSignal;
1562
+ }
1563
+ /**
1564
+ * Look a transaction up in a block explorer by its refHash.
1565
+ *
1566
+ * Resolves `null` when the explorer does not have it — not indexed yet, or never put in a
1567
+ * block. The explorer only knows a transaction once a block has decided it, so for one
1568
+ * you have just sent, the node socket answers first.
1569
+ */
1570
+ declare function getExplorerTransaction(request: ExplorerTransactionRequest): Promise<GeeqExplorerTransaction | null>;
1571
+
1572
+ /** What a WebSocket hands its `message` listeners, narrowed to the one field read. */
1573
+ interface GeeqWebSocketMessageEvent {
1574
+ readonly data: unknown;
1575
+ }
1576
+ /**
1577
+ * A WebSocket, narrowed to what this SDK uses.
1578
+ *
1579
+ * Deliberately structural, like {@link GeeqTransport}: a browser `WebSocket`, Node 22's
1580
+ * global one and the `ws` package's class all satisfy it with no adapter, and so does a
1581
+ * scripted fake in a test. Listeners are attached with `addEventListener` rather than
1582
+ * `onmessage` and friends because a method is compared bivariantly, which is what lets
1583
+ * each implementation's own event types through.
1584
+ */
1585
+ interface GeeqWebSocketLike {
1586
+ readonly readyState: number;
1587
+ send(data: string): void;
1588
+ close(code?: number, reason?: string): void;
1589
+ addEventListener(type: 'open', listener: (event: unknown) => void): void;
1590
+ addEventListener(type: 'message', listener: (event: GeeqWebSocketMessageEvent) => void): void;
1591
+ addEventListener(type: 'close', listener: (event: unknown) => void): void;
1592
+ addEventListener(type: 'error', listener: (event: unknown) => void): void;
1593
+ }
1594
+ /**
1595
+ * How the SDK opens a socket.
1596
+ *
1597
+ * `(url) => new WebSocket(url)` is the whole contract. Pass one to use the `ws` package on
1598
+ * Node 20, to add headers or a TLS agent there, or to script a connection in a test.
1599
+ */
1600
+ type GeeqWebSocketFactory = (url: string) => GeeqWebSocketLike;
1601
+ /**
1602
+ * `new globalThis.WebSocket(url)`, resolved at call time.
1603
+ *
1604
+ * Never a stored reference, for the same reason as `defaultTransport`: a test that swaps
1605
+ * the global after import must still be honoured. Node 20 has no global WebSocket at all,
1606
+ * and a library that supports Node 20 cannot assume one, so its absence is reported with
1607
+ * the one-line fix rather than as a `TypeError`.
1608
+ */
1609
+ declare const defaultWebSocketFactory: GeeqWebSocketFactory;
1610
+
1611
+ /**
1612
+ * The four verdicts a node reports for a transaction, by their protocol names.
1613
+ *
1614
+ * - `valid` — committed in a block. Final.
1615
+ * - `invalid` — placed in a block's invalid list, typically a nonce or solvency failure
1616
+ * found at commit. Final, and **the sender is still charged the fee**.
1617
+ * - `incorrectlyIncluded` — carried by a node the chain judged dishonest. Final; not the
1618
+ * sender's fault.
1619
+ * - `nonsane` — refused by the receiving node, at intake (alongside an HTTP 400) or at block
1620
+ * selection (after a 200). The node drops it, so it is final in practice — but a node
1621
+ * restarting can carry its buffer forward, and then a later verdict follows.
1622
+ *
1623
+ * From geeq_node_core `UutStatuses` (actors/interfaces.ts).
1624
+ */
1625
+ type GeeqTxVerdict = 'nonsane' | 'incorrectlyIncluded' | 'invalid' | 'valid';
1626
+ /**
1627
+ * One `uut_status` event, normalised.
1628
+ *
1629
+ * Every field is what the node sent, renamed only where the wire name misleads. Nothing
1630
+ * is inferred: in particular `vlbBlock` is passed through as reported and not treated as
1631
+ * the inclusion block, because on current nodes it is one ahead of it for `valid` and
1632
+ * `invalid` events.
1633
+ */
1634
+ interface GeeqTxStatusEvent {
1635
+ /**
1636
+ * The transaction's refHash, standard base64 — compare it with `transactionIdOf(tx)`.
1637
+ * Always the transaction hash, never the payload hash.
1638
+ */
1639
+ readonly id: string;
1640
+ readonly verdict: GeeqTxVerdict;
1641
+ /** The node's own boolean: true exactly when `verdict` is `valid`. */
1642
+ readonly isValid: boolean;
1643
+ /** The node's reason, verbatim. Empty when valid. Show it; never parse it. */
1644
+ readonly error: string | undefined;
1645
+ /** The transaction's own target block, as the node reports it. */
1646
+ readonly blockNumber: number | undefined;
1647
+ /** The node's `vlbBlock`, as reported. See the note above before relying on it. */
1648
+ readonly vlbBlock: number | undefined;
1649
+ /**
1650
+ * `outgoing` on the sender's copy, `incoming` on the receiver's, when the node says —
1651
+ * older nodes do not. Do not use it to tell your own transactions apart: match on
1652
+ * {@link id} instead.
1653
+ */
1654
+ readonly direction: 'incoming' | 'outgoing' | undefined;
1655
+ /** The signer's base64 public key. */
1656
+ readonly signer: string | undefined;
1657
+ /** When the node stamped it. Second precision. */
1658
+ readonly createdAt: Date | undefined;
1659
+ /** The event's payload exactly as it arrived, for logging and support. */
1660
+ readonly raw: unknown;
1661
+ }
1662
+ /**
1663
+ * A transaction's id as status events name it: its `transactionHash`, in base64.
1664
+ *
1665
+ * Works on anything the SDK builds — an attestation or a transfer — because each already
1666
+ * carries the hash its kind is identified by, and the two kinds are hashed differently.
1667
+ */
1668
+ declare function transactionIdOf(tx: {
1669
+ readonly transactionHash: Uint8Array;
1670
+ }): string;
1671
+
1672
+ /** The node committed it. */
1673
+ interface GeeqTxConfirmed {
1674
+ readonly status: 'confirmed';
1675
+ readonly id: string;
1676
+ /** The `valid` event that settled it. */
1677
+ readonly event: GeeqTxStatusEvent;
1678
+ readonly at: Date;
1679
+ }
1680
+ /**
1681
+ * Why a transaction was rejected. Branch on this, never on {@link GeeqTxRejected.message}.
1682
+ *
1683
+ * - `invalid` — found wrong at commit, usually the nonce or the balance. The fee is still
1684
+ * charged.
1685
+ * - `incorrectly-included` — carried by a node the chain judged dishonest. Not your fault.
1686
+ * - `nonsane` — the receiving node refused it and dropped it.
1687
+ * - `refused` — the node answered the submission with an error status. It never entered
1688
+ * a block and will produce no status event.
1689
+ */
1690
+ type GeeqTxRejectionReason = 'invalid' | 'incorrectly-included' | 'nonsane' | 'refused';
1691
+ interface GeeqTxRejected {
1692
+ readonly status: 'rejected';
1693
+ readonly id: string;
1694
+ readonly reason: GeeqTxRejectionReason;
1695
+ /** The node's own words when it gave any. For display and support — never parse it. */
1696
+ readonly message: string;
1697
+ /** The event that settled it. Undefined for a `refused` submission the socket never saw. */
1698
+ readonly event: GeeqTxStatusEvent | undefined;
1699
+ /** The HTTP status of a `refused` submission, when the caller reported one. */
1700
+ readonly httpStatus: number | undefined;
1701
+ readonly at: Date;
1702
+ }
1703
+ /**
1704
+ * No verdict, and none is coming to this handle.
1705
+ *
1706
+ * - `timeout` — nothing final arrived in time. **This is not a rejection**: statuses sent
1707
+ * while the socket was reconnecting are lost, so it may well have settled. Read the
1708
+ * ledger (has the account's nonce moved on?) before sending it again.
1709
+ * - `closed` — the socket was disposed while it was pending.
1710
+ * - `released` — the caller stopped tracking it.
1711
+ */
1712
+ type GeeqTxUnknownReason = 'timeout' | 'closed' | 'released';
1713
+ interface GeeqTxUnknown {
1714
+ readonly status: 'unknown';
1715
+ readonly id: string;
1716
+ readonly reason: GeeqTxUnknownReason;
1717
+ readonly message: string;
1718
+ readonly at: Date;
1719
+ }
1720
+ type GeeqTxOutcome = GeeqTxConfirmed | GeeqTxRejected | GeeqTxUnknown;
1721
+ /** `pending` until an outcome, then the outcome's own status. Never goes back. */
1722
+ type GeeqTxState = 'pending' | GeeqTxOutcome['status'];
1723
+ /** What a submission refusal looked like, for {@link GeeqTrackedTx.refused}. */
1724
+ interface GeeqTxRefusal {
1725
+ /** The HTTP status the node answered with. */
1726
+ readonly status?: number;
1727
+ /** The node's `details`, or the body — whatever it said. */
1728
+ readonly message?: string;
1729
+ }
1730
+ /** @internal */
1731
+ interface TrackedTxInit {
1732
+ readonly id: string;
1733
+ readonly timeoutMs: number;
1734
+ readonly nonsaneGraceMs: number;
1735
+ /** Called once, when it settles — the socket drops it from its table. */
1736
+ readonly detach: () => void;
1737
+ }
1738
+ /**
1739
+ * One submitted transaction, followed to its verdict.
1740
+ *
1741
+ * Returned by {@link GeeqSocket.track}. It collapses the node's four verdicts, some of
1742
+ * which arrive more than once and one of which is only usually final, into one of three
1743
+ * outcomes an application can branch on — and it never rejects, because a rejected
1744
+ * transaction is an answer, not a failure:
1745
+ *
1746
+ * ```ts
1747
+ * const outcome = await tracked.settled()
1748
+ * if (outcome.status === 'confirmed') …
1749
+ * else if (outcome.status === 'rejected') show(outcome.reason, outcome.message)
1750
+ * else checkLedgerBeforeRetrying() // 'unknown'
1751
+ * ```
1752
+ *
1753
+ * In a reactive framework, keep it raw (Vue's `markRaw`) and re-read `status` and
1754
+ * `events` from {@link onChange}.
1755
+ */
1756
+ declare class GeeqTrackedTx {
1757
+ /** The transaction's refHash, base64 — the same string status events carry. */
1758
+ readonly id: string;
1759
+ private readonly received;
1760
+ private readonly listeners;
1761
+ private readonly nonsaneGraceMs;
1762
+ private readonly detach;
1763
+ private readonly promise;
1764
+ private readonly resolve;
1765
+ private current;
1766
+ private deadline;
1767
+ private grace;
1768
+ /** @internal */
1769
+ constructor(init: TrackedTxInit);
1770
+ /** `pending`, then `confirmed`, `rejected` or `unknown` — once, and for good. */
1771
+ get status(): GeeqTxState;
1772
+ /** The outcome, once there is one. */
1773
+ get outcome(): GeeqTxOutcome | undefined;
1774
+ /** Every status event received for this transaction, oldest first. */
1775
+ get events(): readonly GeeqTxStatusEvent[];
1776
+ /** Resolves with the outcome. Never rejects — see the class notes. */
1777
+ settled(): Promise<GeeqTxOutcome>;
1778
+ /**
1779
+ * Call `listener` whenever an event arrives for this transaction or it settles.
1780
+ *
1781
+ * Returns the function that removes it. Read {@link status}, {@link events} and
1782
+ * {@link outcome} inside; they are already up to date.
1783
+ */
1784
+ onChange(listener: (tx: GeeqTrackedTx) => void): () => void;
1785
+ /**
1786
+ * Report that the node refused the submission — answered it with an error status.
1787
+ *
1788
+ * Settles it `rejected` at once. A refused transaction never reaches a block, so no
1789
+ * status event would ever arrive to settle it, and waiting would only turn a clear
1790
+ * answer into a timeout. Do **not** call this when the POST failed to arrive at all
1791
+ * (a network error): the node may have it, so leave it tracked.
1792
+ */
1793
+ refused(refusal?: GeeqTxRefusal): void;
1794
+ /** Stop tracking. Settles `unknown` with reason `released` if it was still pending. */
1795
+ release(): void;
1796
+ /** @internal Settle as unknown because the socket is going away, or on release. */
1797
+ abandon(reason: 'closed' | 'released', message: string): void;
1798
+ /**
1799
+ * @internal Take one status event for this transaction.
1800
+ *
1801
+ * The only place the protocol's rules live. First-event-wins would report false
1802
+ * failures: a `nonsane` can be followed by a real verdict when a node carries its buffer
1803
+ * over a restart, so it only settles if nothing final follows within the grace window.
1804
+ * Everything after the outcome — a second node's copy, a duplicate — is ignored.
1805
+ */
1806
+ receive(event: GeeqTxStatusEvent): void;
1807
+ private finish;
1808
+ private notify;
1809
+ toJSON(): Record<string, unknown>;
1810
+ toString(): string;
1811
+ }
1812
+
1813
+ /**
1814
+ * - `connecting` — the first handshake, inside {@link GeeqSocket.connect}.
1815
+ * - `open` — subscribed and receiving.
1816
+ * - `reconnecting` — dropped after being open; retrying with backoff. Anything the node
1817
+ * sends meanwhile is lost.
1818
+ * - `closed` — disposed. Final.
1819
+ */
1820
+ type GeeqSocketState = 'connecting' | 'open' | 'reconnecting' | 'closed';
1821
+ /** One JSON frame from the node, as `{ event, payload }`. */
1822
+ interface GeeqSocketMessage {
1823
+ readonly event: string;
1824
+ readonly payload: unknown;
1825
+ }
1826
+ interface GeeqSocketOptions {
1827
+ /**
1828
+ * The node the socket belongs to — normally `chain.endpoint`, which is also where the
1829
+ * transactions go. Its socket is derived: `https://ip:2082` becomes
1830
+ * `wss://ip:2082/ws/`.
1831
+ *
1832
+ * Supply this or {@link url}, not both. Use the node you submit to: only that node
1833
+ * reports a `nonsane` refusal, though every node reports the final verdicts.
1834
+ */
1835
+ readonly endpoint?: string | URL;
1836
+ /**
1837
+ * The socket URL itself, when it is not the one derived from the node — a local
1838
+ * harness reaching the socket server directly on its own port, for instance.
1839
+ */
1840
+ readonly url?: string | URL;
1841
+ /** Defaults to `new globalThis.WebSocket(url)`. Supply one on Node 20 — see README. */
1842
+ readonly webSocket?: GeeqWebSocketFactory;
1843
+ /**
1844
+ * How long a handshake, or a send waiting on one, may take. Defaults to 15000. 0 waits
1845
+ * for ever — including a first handshake that neither opens nor fails.
1846
+ */
1847
+ readonly openTimeoutMs?: number;
1848
+ /** How often to send the keepalive `'ping'`. Defaults to 25000; 0 disables it. */
1849
+ readonly heartbeatMs?: number;
1850
+ /** Reconnect after a drop. Defaults to true. */
1851
+ readonly reconnect?: boolean;
1852
+ /** The first reconnect delay, doubled on each failure. Defaults to 1000. */
1853
+ readonly reconnectDelayMs?: number;
1854
+ /** The ceiling on that delay. Defaults to 30000. */
1855
+ readonly maxReconnectDelayMs?: number;
1856
+ /** Abandons the first handshake. Once connected, use {@link GeeqSocket.dispose}. */
1857
+ readonly signal?: AbortSignal;
1858
+ }
1859
+ interface GeeqSendOptions {
1860
+ /**
1861
+ * Re-send the frame on every reconnect. A node forgets a connection's subscriptions
1862
+ * when it drops, so anything that subscribes should set this. Only the latest frame for
1863
+ * each event name is kept.
1864
+ */
1865
+ readonly replay?: boolean;
1866
+ }
1867
+ interface GeeqListenOptions {
1868
+ readonly blockNumber?: number;
1869
+ /** Defaults to true. */
1870
+ readonly now?: boolean;
1871
+ }
1872
+ interface GeeqTrackOptions {
1873
+ /** How long to wait for a final status. Defaults to 300000. 0 waits for ever. */
1874
+ readonly timeoutMs?: number;
1875
+ /** How long a `nonsane` may be overturned by a real verdict. Defaults to 30000. */
1876
+ readonly nonsaneGraceMs?: number;
1877
+ /** More accounts to subscribe alongside the signer. */
1878
+ readonly accounts?: readonly (AccountRef | string)[];
1879
+ }
1880
+ /**
1881
+ * Anything with the hash a status event names. Every transaction this SDK builds fits,
1882
+ * and brings its signer's `publicKey` for the subscription.
1883
+ */
1884
+ interface GeeqTrackable {
1885
+ readonly transactionHash: Uint8Array;
1886
+ readonly publicKey?: Uint8Array;
1887
+ }
1888
+ /**
1889
+ * A node's WebSocket: the channel through which it reports what happened to the
1890
+ * transactions you sent, and pushes account and chain updates.
1891
+ *
1892
+ * One class for every use an application has for the socket:
1893
+ *
1894
+ * - {@link track} a transaction you are about to submit, and get its verdict;
1895
+ * - {@link subscribe} to accounts and read their events with {@link on};
1896
+ * - {@link send} any frame the node understands, and receive any it sends.
1897
+ *
1898
+ * The protocol's hazards are handled here, once: subscriptions replace rather than add,
1899
+ * so the full set of keys is always sent; the node forgets a connection's subscriptions
1900
+ * when it drops, so they are replayed on every reconnect before anything is announced;
1901
+ * the edge proxy drops an idle socket, so it is kept alive; a transaction's first status
1902
+ * is not always its last, so {@link GeeqTrackedTx} decides when it is final.
1903
+ *
1904
+ * ```ts
1905
+ * const socket = await GeeqSocket.connect({ endpoint: chain.endpoint })
1906
+ * const tx = await chain.createTransfer({ from, to, amount })
1907
+ * const tracked = await socket.track(tx) // before submitting — always
1908
+ * const response = await fetch(submitRoute(tx.nodeUrl), { … })
1909
+ * if (!response.ok) tracked.refused({ status: response.status })
1910
+ * const outcome = await tracked.settled()
1911
+ * ```
1912
+ *
1913
+ * In a reactive framework, mark an instance raw (Vue's `markRaw`) rather than letting it
1914
+ * be proxied — a proxied native WebSocket throws on every call.
1915
+ */
1916
+ declare class GeeqSocket {
1917
+ /** The socket URL, normalised. */
1918
+ readonly url: string;
1919
+ private readonly factory;
1920
+ private readonly openTimeoutMs;
1921
+ private readonly heartbeatMs;
1922
+ private readonly reconnects;
1923
+ private readonly reconnectDelayMs;
1924
+ private readonly maxReconnectDelayMs;
1925
+ /** The frames in force, by event name, replayed on every connection. */
1926
+ private readonly persisted;
1927
+ /** What this connection has already been told, so a repeat is not sent twice. */
1928
+ private readonly told;
1929
+ private readonly keys;
1930
+ private readonly tracked;
1931
+ private readonly eventListeners;
1932
+ private readonly messageListeners;
1933
+ private readonly statusListeners;
1934
+ private readonly stateListeners;
1935
+ private readonly waiters;
1936
+ private socket;
1937
+ private current;
1938
+ private everOpened;
1939
+ private attempts;
1940
+ private openTimer;
1941
+ private reconnectTimer;
1942
+ private heartbeatTimer;
1943
+ /** @internal */
1944
+ constructor(init: {
1945
+ url: string;
1946
+ factory: GeeqWebSocketFactory;
1947
+ openTimeoutMs: number;
1948
+ heartbeatMs: number;
1949
+ reconnect: boolean;
1950
+ reconnectDelayMs: number;
1951
+ maxReconnectDelayMs: number;
1952
+ });
1953
+ /**
1954
+ * Open a node's socket and resolve once it is open.
1955
+ *
1956
+ * Throws `socket-unreachable` if the first handshake fails, rather than retrying
1957
+ * quietly: a socket that never opened is almost always a certificate, a mixed-content
1958
+ * page or a wrong URL, and retrying hides which. After that first open, a drop is
1959
+ * retried automatically and reported through {@link onConnectionChange}.
1960
+ */
1961
+ static connect(options: GeeqSocketOptions): Promise<GeeqSocket>;
1962
+ /** Where the connection stands. See {@link GeeqSocketState}. */
1963
+ get state(): GeeqSocketState;
1964
+ /** Every account key subscribed so far, base64. The set only grows. */
1965
+ get subscriptions(): readonly string[];
1966
+ /**
1967
+ * Subscribe to accounts' events: their transactions' statuses and record updates.
1968
+ *
1969
+ * Takes a keypair, a `{ publicKey }`, a 32-byte account id, a base64 key, or a list of
1970
+ * them. The node has no unsubscribe and treats every subscription as a replacement, so
1971
+ * this adds to the set this socket holds and sends all of it — calling it for one
1972
+ * account never silences another. Resolves once the node has been told.
1973
+ */
1974
+ subscribe(accounts: AccountRef | string | readonly (AccountRef | string)[]): Promise<void>;
1975
+ /** Ask for events from now, or from a block. Replayed on reconnect. */
1976
+ listen(options?: GeeqListenOptions): Promise<void>;
1977
+ /**
1978
+ * Subscribe to the whole chain's record feed — every account, fee and parameter update.
1979
+ * What a block explorer or an indexer wants; an ordinary application does not.
1980
+ */
1981
+ subscribeBlockExplorer(): Promise<void>;
1982
+ /**
1983
+ * Send a frame, `{ event, payload }`, waiting for the connection if it is down.
1984
+ *
1985
+ * The escape hatch for anything the node understands that this class has no method
1986
+ * for. Rejects with `socket-unreachable` if the socket does not reopen within
1987
+ * `openTimeoutMs` — a replayed frame is still kept, and goes out when it does.
1988
+ */
1989
+ send(event: string, payload?: unknown, options?: GeeqSendOptions): Promise<void>;
1990
+ /**
1991
+ * Follow a transaction to its verdict. Call it **before** submitting.
1992
+ *
1993
+ * Subscribes the signer — the node pushes a transaction's statuses only to connections
1994
+ * subscribed to its account — and resolves once the node has been told, so a status
1995
+ * cannot arrive before anyone is listening for it. If this throws, do not submit: a
1996
+ * transaction sent with no way to hear its verdict leaves the user guessing.
1997
+ *
1998
+ * Tracking the same transaction twice returns the same handle.
1999
+ */
2000
+ track(tx: GeeqTrackable, options?: GeeqTrackOptions): Promise<GeeqTrackedTx>;
2001
+ /**
2002
+ * Call `listener` with the payload of every frame named `event`, whatever it is —
2003
+ * `'uut_status'`, `'coin-account-update'`, `'fee-update'`, or one this SDK has never
2004
+ * heard of. Returns the function that removes it.
2005
+ */
2006
+ on(event: string, listener: (payload: unknown) => void): () => void;
2007
+ /** Call `listener` with every JSON frame the node sends. */
2008
+ onMessage(listener: (message: GeeqSocketMessage) => void): () => void;
2009
+ /** Call `listener` with every transaction status, tracked or not, already parsed. */
2010
+ onTxStatus(listener: (event: GeeqTxStatusEvent) => void): () => void;
2011
+ /** Call `listener` whenever {@link state} changes. */
2012
+ onConnectionChange(listener: (state: GeeqSocketState) => void): () => void;
2013
+ /**
2014
+ * Close for good. Pending sends reject with `socket-closed`, and every transaction still
2015
+ * being tracked settles `unknown` with reason `closed` — it may yet be decided, just
2016
+ * not heard about here.
2017
+ */
2018
+ dispose(): void;
2019
+ toJSON(): Record<string, unknown>;
2020
+ toString(): string;
2021
+ private assertOpenable;
2022
+ private setState;
2023
+ /** Resolves when the socket is open, or rejects after `openTimeoutMs` or on dispose. */
2024
+ private whenOpen;
2025
+ private dial;
2026
+ private opened;
2027
+ /** A handshake failed, or an open socket dropped. */
2028
+ private attemptFailed;
2029
+ /**
2030
+ * Put one frame on the current connection. True once it is there — or already was.
2031
+ *
2032
+ * False means the connection is gone. If our books still said it was open, it is
2033
+ * treated as the drop it is, here and now, so the caller waits for the next connection
2034
+ * instead of spinning on this one.
2035
+ */
2036
+ private transmit;
2037
+ private receive;
2038
+ private startHeartbeat;
2039
+ private stopHeartbeat;
2040
+ }
2041
+
2042
+ /** What one metadata entry costs, and whether the chain would take it. */
2043
+ interface MetadataEntryReport {
2044
+ readonly index: number;
2045
+ /** Length once encoded as UTF-8, which is what the limit is measured in. */
2046
+ readonly bytes: number;
2047
+ readonly overLimit: boolean;
2048
+ }
2049
+ interface MetadataReport {
2050
+ readonly entries: readonly MetadataEntryReport[];
2051
+ readonly count: number;
2052
+ readonly tooManyEntries: boolean;
2053
+ readonly withinLimits: boolean;
2054
+ /** Undefined when the chain publishes no limits, in which case nothing is checked. */
2055
+ readonly limits: GeeqMetadataLimits | undefined;
2056
+ }
2057
+ /**
2058
+ * Measure metadata against a chain's limits without throwing.
2059
+ *
2060
+ * The same rules {@link assertMetadataWithinLimits} enforces, reported rather than
2061
+ * raised, so a UI can colour a field as someone types instead of waiting for a failed
2062
+ * build. Byte lengths are UTF-8, which is the only measure that matters here — a label
2063
+ * of ten emoji is forty bytes, and counting characters would call it short.
2064
+ */
2065
+ declare function inspectMetadata(metadata: readonly (string | Uint8Array)[] | undefined, limits: GeeqMetadataLimits | undefined): MetadataReport;
2066
+
2067
+ declare function toBase64(bytes: Uint8Array): string;
2068
+ /**
2069
+ * Decode base64, rejecting anything the alphabet does not cover.
2070
+ *
2071
+ * `label` names the thing being decoded so the message points at the caller's data
2072
+ * rather than at this function.
2073
+ */
2074
+ declare function fromBase64(value: string, label?: string): Uint8Array;
2075
+
2076
+ declare function toHex(bytes: Uint8Array): string;
2077
+ declare function fromHex(value: string, label?: string): Uint8Array;
2078
+
2079
+ interface DecodeLedgerOptions {
2080
+ /** The node this came from, carried through onto the snapshot. */
2081
+ readonly nodeUrl?: string | undefined;
2082
+ /** Overrides the read timestamp. For deterministic tests. */
2083
+ readonly readAt?: number | undefined;
2084
+ }
2085
+ /**
2086
+ * Decode a validation layer ledger.
2087
+ *
2088
+ * `source` is what a node's `result` field holds: base64, or the bytes behind it. The
2089
+ * message is Cap'n Proto **packed**, which is what the node writes with
2090
+ * `toPackedArrayBuffer()` — not the flattened form used for signing.
2091
+ *
2092
+ * Everything is read in this one pass and the message is then dropped, so the caller
2093
+ * gets plain values with no traversal budget attached. See {@link GeeqLedger}.
2094
+ */
2095
+ declare function decodeValidationLedger(source: Uint8Array | ArrayBuffer | string, options?: DecodeLedgerOptions): GeeqLedger;
2096
+
2097
+ /**
2098
+ * Build and sign an attestation transaction. Synchronous; requires a loaded engine.
2099
+ *
2100
+ * This function is offline: it opens no sockets and reads no ledger. It needs chain
2101
+ * state that only a node can supply, so either pass the four values yourself or let
2102
+ * {@link GeeqChain} read them and hand you a `chainState`.
2103
+ *
2104
+ * The transaction is signed but **not submitted**. Post
2105
+ * {@link AttestationTx.transaction} and {@link AttestationTx.payload} to the node named
2106
+ * by {@link AttestationTx.nodeUrl}.
2107
+ */
2108
+ declare function createAttestationTxSync(input: AttestationTxInput): AttestationTx;
2109
+ /** Build and sign an attestation transaction, loading the engine if needed. */
2110
+ declare function createAttestationTx(input: AttestationTxInput): Promise<AttestationTx>;
2111
+
2112
+ /**
2113
+ * Build and sign a coin transfer. Synchronous; requires a loaded engine.
2114
+ *
2115
+ * Unlike an attestation this is a **validation layer** transaction: it carries no
2116
+ * separate payload, so there is one base64 blob to submit rather than two, and it is
2117
+ * posted as type `uvt`.
2118
+ *
2119
+ * NFTs ride along in the same transaction, with coin: the chain's minimum transfer
2120
+ * applies even when the point is the NFTs.
2121
+ *
2122
+ * Offline — it opens no sockets and reads no ledger. It cannot check that the sender can
2123
+ * afford the transfer, that the amount clears the chain's minimum, or that it holds the
2124
+ * NFTs; all three need a ledger. {@link GeeqChain.createTransfer} does check them.
2125
+ */
2126
+ declare function createCoinTransferTxSync(input: CoinTransferTxInput): CoinTransferTx;
2127
+ /** Build and sign a coin transfer, loading the engine if needed. */
2128
+ declare function createCoinTransferTx(input: CoinTransferTxInput): Promise<CoinTransferTx>;
2129
+
2130
+ /**
2131
+ * Build and sign a transaction that creates an NFT mint proxy. Synchronous; requires a
2132
+ * loaded engine.
2133
+ *
2134
+ * A proxy is a record of its own on the chain. It holds coin, carries its own nonce, and
2135
+ * names one admin — the signer here — who is the only key that can mint NFTs through it.
2136
+ * The signer pays `amount` onto it, plus the fee.
2137
+ *
2138
+ * Offline: it cannot check that the index is unused or that the signer can afford it.
2139
+ * {@link GeeqChain.createNftMintProxy} does both.
2140
+ */
2141
+ declare function createNftMintProxyTxSync(input: NftMintProxyTxInput): NftMintProxyTx;
2142
+ /** Build and sign a proxy creation, loading the engine if needed. */
2143
+ declare function createNftMintProxyTx(input: NftMintProxyTxInput): Promise<NftMintProxyTx>;
2144
+
2145
+ /**
2146
+ * Build and sign an NFT mint. Synchronous; requires a loaded engine.
2147
+ *
2148
+ * An NFT is minted through a proxy, and the proxy is the sender: it pays the fee and
2149
+ * `amount`, which goes to the receiver with the NFT, and it spends its own nonce. Its
2150
+ * admin — the signer here — is the only key the chain accepts for it. The NFT itself is an
2151
+ * attestation subgroup, as an attestation carries: the content's hash and its labels,
2152
+ * signed by the admin and naming the proxy.
2153
+ *
2154
+ * Offline: it cannot check that the proxy exists, is the signer's, or can pay.
2155
+ * {@link GeeqChain.mintNft} does all three.
2156
+ */
2157
+ declare function createNftMintTxSync(input: NftMintTxInput): NftMintTx;
2158
+ /** Build and sign an NFT mint, loading the engine if needed. */
2159
+ declare function createNftMintTx(input: NftMintTxInput): Promise<NftMintTx>;
2160
+
2161
+ /**
2162
+ * Every error thrown by this SDK is a {@link GeeqError} carrying a stable
2163
+ * {@link GeeqErrorCode}. Branch on `code`, never on the message text.
2164
+ */
2165
+ type GeeqErrorCode =
2166
+ /** An SDK function was called before the engine finished loading. */
2167
+ 'not-initialized'
2168
+ /** The WebAssembly engine could not be loaded. */
2169
+ | 'init-failed'
2170
+ /** A public or private key was the wrong length, or the pair did not match. */
2171
+ | 'invalid-key'
2172
+ /** A wallet address was malformed, or encoded a record type we do not know. */
2173
+ | 'invalid-address'
2174
+ /** A caller-supplied argument was outside its allowed range. */
2175
+ | 'invalid-argument'
2176
+ /** A node could not be reached at all — no HTTP response arrived. */
2177
+ | 'network-unreachable'
2178
+ /** A node, simulator or explorer answered with an error status. See `status` and `nodeDetails`. */
2179
+ | 'node-error'
2180
+ /** A node answered, but its ledger could not be decoded. */
2181
+ | 'ledger-unreadable'
2182
+ /** The node's chain is not the one the caller said to expect. */
2183
+ | 'chain-mismatch'
2184
+ /** The signing account has no record on this chain yet — it needs funding. */
2185
+ | 'account-not-found'
2186
+ /** The account exists but its record type cannot sign this transaction. */
2187
+ | 'account-not-attestable'
2188
+ /**
2189
+ * The node being targeted cannot be identified, or cannot process transactions. Also
2190
+ * thrown when a simulator or explorer answers with something it should not serve.
2191
+ */
2192
+ | 'node-unusable'
2193
+ /** Metadata exceeds a limit this chain publishes in its ledger. */
2194
+ | 'metadata-too-large'
2195
+ /** A second transaction was built for one account against one block. */
2196
+ | 'nonce-collision'
2197
+ /** The sending account does not hold enough coin. */
2198
+ | 'insufficient-funds'
2199
+ /** The amount is below the smallest transfer this chain accepts. */
2200
+ | 'amount-below-minimum'
2201
+ /** The sending account does not hold an NFT it was asked to send. */
2202
+ | 'nft-not-owned'
2203
+ /** A node's WebSocket could not be opened, or this runtime has no WebSocket at all. */
2204
+ | 'socket-unreachable'
2205
+ /** The {@link GeeqSocket} was disposed, so it will not send or receive again. */
2206
+ | 'socket-closed';
2207
+ interface GeeqErrorOptions {
2208
+ readonly cause?: unknown;
2209
+ /** HTTP status, when a server answered. */
2210
+ readonly status?: number;
2211
+ /**
2212
+ * The server's own `details`, verbatim; a list of them is joined with `; `. Never parse
2213
+ * it — branch on `code`.
2214
+ */
2215
+ readonly nodeDetails?: string;
2216
+ }
2217
+ declare class GeeqError extends Error {
2218
+ readonly code: GeeqErrorCode;
2219
+ readonly status?: number | undefined;
2220
+ readonly nodeDetails?: string | undefined;
2221
+ constructor(code: GeeqErrorCode, message: string, options?: GeeqErrorOptions);
2222
+ }
2223
+
2224
+ export { ACCOUNT_ID_BYTES, ATTEST_HASH_BYTES, type AccountIdentifier, type AccountRef, type AttestationContent, type AttestationRequest, type AttestationTx, type AttestationTxFlatInput, type AttestationTxInput, type AttestationTxStateInput, type ChainStateInput, type ChainStateRequest, type CoinTransferContent, type CoinTransferRequest, type CoinTransferTx, type CoinTransferTxFlatInput, type CoinTransferTxInput, type CoinTransferTxStateInput, type CreateWalletAddressInput, DEFAULT_RECORD_TYPE, type DecodeLedgerOptions, type DecodedWalletAddress, type Discovery, type ExplorerTransactionRequest, GEEQ_NODE_PORT, GEEQ_RECORD_TYPES, type GeeqAmount, GeeqChain, type GeeqChainOptions, type GeeqChainState, type GeeqCoinAccount, type GeeqCoinRecordKind, type GeeqCoinType, type GeeqContent, type GeeqDiscoveredChain, type GeeqDiscoveredNode, GeeqError, type GeeqErrorCode, type GeeqExplorerTransaction, type GeeqFee, type GeeqHeldNft, type GeeqHttpResponse, type GeeqKeypair, GeeqLedger, type GeeqLedgerNode, type GeeqListenOptions, type GeeqMetadataLimits, type GeeqNft, type GeeqNftMintProxies, type GeeqNftMintProxy, GeeqNode, type GeeqNodeKind, type GeeqNodeRole, type GeeqNodeStatus, type GeeqProxyAccount, type GeeqProxyKind, type GeeqRecordType, type GeeqRequestInit, type GeeqSendOptions, GeeqSocket, type GeeqSocketMessage, type GeeqSocketOptions, type GeeqSocketState, type GeeqTrackOptions, type GeeqTrackable, GeeqTrackedTx, type GeeqTransport, type GeeqTxConfirmed, type GeeqTxOutcome, type GeeqTxRefusal, type GeeqTxRejected, type GeeqTxRejectionReason, type GeeqTxState, type GeeqTxStatusEvent, type GeeqTxUnknown, type GeeqTxUnknownReason, type GeeqTxVerdict, type GeeqWebSocketFactory, type GeeqWebSocketLike, type GeeqWebSocketMessageEvent, type GenerateMnemonicOptions, type InitOptions, MNEMONIC_WORD_COUNTS, type MetadataEntryReport, type MetadataReport, type MnemonicOptions, type MnemonicWordCount, type NftMintProxiesRequest, type NftMintProxyRequest, type NftMintProxyTx, type NftMintProxyTxInput, type NftMintRequest, type NftMintTx, type NftMintTxInput, type NftTransferEntry, type NftsRequest, PUBLIC_KEY_BYTES, type WalletAddressOptions, type WasmSource, accountIdFromPublicKey, assertAttestHash, attest, attestHashOf, attestHashOfSync, attestationSubmitBody, coinTransferSubmitBody, createAttestationTx, createAttestationTxSync, createCoinTransferTx, createCoinTransferTxSync, createNftMintProxyTx, createNftMintProxyTxSync, createNftMintTx, createNftMintTxSync, createWalletAddress, createWalletAddressSync, decodeValidationLedger, decodeWalletAddress, decodeWalletAddressSync, defaultTransport, defaultWebSocketFactory, discover, formatGeeq, fromBase64, fromHex, generateKeypair, generateKeypairSync, generateMnemonic, getExplorerTransaction, importKeypair, importKeypairSync, initChain, initGeeq, inspectMetadata, isGeeqReady, isValidWalletAddress, keypairFromMnemonic, keypairFromMnemonicSync, keypairFromSecretKey, keypairFromSecretKeySync, ledgerRoute, listChains, listNodes, normalizeMnemonic, readContent, selectNode, socketRoute, submitRoute, toBase64, toHex, toPennies, transactionIdOf, transfer, validateMnemonic, verifySignature };