@geeqdev/geeq-sdk 0.0.1 → 0.1.1

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 (122) hide show
  1. package/.idea/geeq_sdk.iml +12 -0
  2. package/.idea/modules.xml +8 -0
  3. package/.idea/vcs.xml +6 -0
  4. package/.nvmrc +1 -0
  5. package/CONTRIBUTING.md +20 -0
  6. package/example/.env.example +4 -0
  7. package/example/README.md +304 -0
  8. package/example/USAGE.md +74 -0
  9. package/example/index.html +12 -0
  10. package/example/package-lock.json +1557 -0
  11. package/example/package.json +25 -0
  12. package/example/scripts/browser-check.mjs +1421 -0
  13. package/example/scripts/match-derivation.mjs +65 -0
  14. package/example/src/App.vue +138 -0
  15. package/example/src/components/AuthorityPanel.vue +277 -0
  16. package/example/src/components/ChainPanel.vue +267 -0
  17. package/example/src/components/ChainState.vue +59 -0
  18. package/example/src/components/CopyButton.vue +25 -0
  19. package/example/src/components/CreateAssetPanel.vue +313 -0
  20. package/example/src/components/DigitalAssetsPanel.vue +68 -0
  21. package/example/src/components/EvidencePanel.vue +396 -0
  22. package/example/src/components/ExplorerPanel.vue +119 -0
  23. package/example/src/components/KeypairPanel.vue +182 -0
  24. package/example/src/components/PhrasePanel.vue +105 -0
  25. package/example/src/components/SocketWarning.vue +18 -0
  26. package/example/src/components/StepGate.vue +24 -0
  27. package/example/src/components/TransferPanel.vue +416 -0
  28. package/example/src/components/TxDeadline.vue +19 -0
  29. package/example/src/components/TxFee.vue +31 -0
  30. package/example/src/components/TxStatus.vue +105 -0
  31. package/example/src/composables/useSocketConnection.ts +80 -0
  32. package/example/src/env.d.ts +12 -0
  33. package/example/src/main.ts +5 -0
  34. package/example/src/pennies.ts +7 -0
  35. package/example/src/style.css +149 -0
  36. package/example/tsconfig.json +19 -0
  37. package/example/vite.config.ts +26 -0
  38. package/jest.config.js +18 -0
  39. package/package.json +1 -5
  40. package/scripts/copy-wasm.mjs +21 -0
  41. package/scripts/smoke.mjs +382 -0
  42. package/src/address/record-type.ts +77 -0
  43. package/src/address/wallet-address.ts +105 -0
  44. package/src/amount.ts +143 -0
  45. package/src/assert.ts +30 -0
  46. package/src/capnp/README.md +15 -0
  47. package/src/capnp/adminNetTx.capnp.ts +66 -0
  48. package/src/capnp/appLayBlock.capnp.ts +26 -0
  49. package/src/capnp/appLayLedger.capnp.ts +35 -0
  50. package/src/capnp/appLayRec.capnp.ts +159 -0
  51. package/src/capnp/chainStateView.capnp.ts +49 -0
  52. package/src/capnp/coinAcctRec.capnp.ts +193 -0
  53. package/src/capnp/genesisBlock.capnp.ts +56 -0
  54. package/src/capnp/hubContentStmt.capnp.ts +59 -0
  55. package/src/capnp/misc.capnp.ts +19 -0
  56. package/src/capnp/netActorRec.capnp.ts +64 -0
  57. package/src/capnp/nodeContentStmt.capnp.ts +62 -0
  58. package/src/capnp/nodePayloadList.capnp.ts +28 -0
  59. package/src/capnp/nodeTxList.capnp.ts +37 -0
  60. package/src/capnp/paramAcctRec.capnp.ts +669 -0
  61. package/src/capnp/payload.capnp.ts +261 -0
  62. package/src/capnp/proxyAcctRec.capnp.ts +259 -0
  63. package/src/capnp/relayContentStmt.capnp.ts +85 -0
  64. package/src/capnp/sysAcctRec.capnp.ts +47 -0
  65. package/src/capnp/userAppTx.capnp.ts +269 -0
  66. package/src/capnp/userValTx.capnp.ts +1999 -0
  67. package/src/capnp/valLayBlock.capnp.ts +93 -0
  68. package/src/capnp/valLayLedger.capnp.ts +80 -0
  69. package/src/chain/geeq-chain.ts +1094 -0
  70. package/src/chain/geeq-node.ts +105 -0
  71. package/src/chain/nonce-guard.ts +73 -0
  72. package/src/chain/select.ts +149 -0
  73. package/src/crypto/account-id.ts +83 -0
  74. package/src/crypto/keypair.ts +221 -0
  75. package/src/crypto/mnemonic.ts +69 -0
  76. package/src/encoding/base64.ts +58 -0
  77. package/src/encoding/hex.ts +42 -0
  78. package/src/errors.ts +122 -0
  79. package/src/index.ts +195 -0
  80. package/src/ledger/coin-accounts.ts +73 -0
  81. package/src/ledger/decode.ts +147 -0
  82. package/src/ledger/geeq-ledger.ts +184 -0
  83. package/src/ledger/net-actors.ts +129 -0
  84. package/src/ledger/nft-records.ts +49 -0
  85. package/src/ledger/params.ts +82 -0
  86. package/src/ledger/proxy-accounts.ts +51 -0
  87. package/src/net/discovery.ts +231 -0
  88. package/src/net/explorer.ts +123 -0
  89. package/src/net/read-ledger.ts +58 -0
  90. package/src/net/routes.ts +79 -0
  91. package/src/net/transport.ts +221 -0
  92. package/src/net/websocket.ts +94 -0
  93. package/src/runtime/bindings.ts +38 -0
  94. package/src/runtime/init.ts +49 -0
  95. package/src/runtime/wasm-loader.browser.ts +22 -0
  96. package/src/runtime/wasm-loader.node.ts +16 -0
  97. package/src/socket/geeq-socket.ts +827 -0
  98. package/src/socket/tracked-tx.ts +319 -0
  99. package/src/socket/tx-status.ts +131 -0
  100. package/src/tx/attestation.ts +222 -0
  101. package/src/tx/content.ts +82 -0
  102. package/src/tx/encode.ts +107 -0
  103. package/src/tx/expiry.ts +11 -0
  104. package/src/tx/fee.ts +31 -0
  105. package/src/tx/metadata.ts +100 -0
  106. package/src/tx/nft.ts +108 -0
  107. package/src/tx/proxy.ts +156 -0
  108. package/src/tx/transfer.ts +162 -0
  109. package/src/types.ts +503 -0
  110. package/src/wasm/README.md +22 -0
  111. package/src/wasm/browser/geeq_core.d.ts +1224 -0
  112. package/src/wasm/browser/geeq_core.js +6014 -0
  113. package/src/wasm/browser/geeq_core_bg.wasm +0 -0
  114. package/src/wasm/browser/geeq_core_bg.wasm.d.ts +447 -0
  115. package/src/wasm/nodejs/geeq_core.d.ts +753 -0
  116. package/src/wasm/nodejs/geeq_core.js +6016 -0
  117. package/src/wasm/nodejs/geeq_core_bg.wasm +0 -0
  118. package/src/wasm/nodejs/geeq_core_bg.wasm.d.ts +447 -0
  119. package/src/wasm/nodejs/package.json +3 -0
  120. package/tsconfig.build.json +8 -0
  121. package/tsconfig.json +20 -0
  122. package/tsup.config.ts +60 -0
@@ -0,0 +1,162 @@
1
+ import { blake3 } from '@noble/hashes/blake3'
2
+ import { Message } from 'capnp-ts'
3
+ import { UserValTx as CapnpUserValTx } from '../capnp/userValTx.capnp'
4
+
5
+ import { assertUint32 } from '../assert'
6
+ import { penniesToUint64 } from '../amount'
7
+ import { ACCOUNT_ID_BYTES, PUBLIC_KEY_BYTES, resolveAccountId } from '../crypto/account-id'
8
+ import type { AccountRef } from '../crypto/account-id'
9
+ import { toBase64 } from '../encoding/base64'
10
+ import { invalidArgument } from '../errors'
11
+ import { WasmUserValTx, WasmUserValTxData } from '../runtime/bindings'
12
+ import { assertReady, initGeeq } from '../runtime/init'
13
+ import type { CoinTransferTx, CoinTransferTxInput, GeeqKeypair } from '../types'
14
+ import {
15
+ REF_HASH_BYTES,
16
+ assertChainState,
17
+ assertKeypair,
18
+ flatten,
19
+ packed,
20
+ pinning,
21
+ resolveChainState,
22
+ } from './encode'
23
+ import { feeFor } from './fee'
24
+
25
+ /**
26
+ * Sign a built validation-layer transaction and work out the id the node files it under.
27
+ * Every validation-layer transaction — a transfer, a proxy creation — ends this way.
28
+ *
29
+ * @internal
30
+ */
31
+ export function signValidationTx(
32
+ message: Message,
33
+ tx: CapnpUserValTx,
34
+ keypair: GeeqKeypair,
35
+ what: string,
36
+ ): { transaction: string; transactionHash: Uint8Array; bytes: number } {
37
+ // A signature is a union here rather than a plain field, so it has to be told it is
38
+ // absent before it can be told what it is.
39
+ tx.initSignature().setNone()
40
+ tx.initEndorsements(0)
41
+
42
+ // Signed over the signature-EXCLUDED flatten, which is what the node verifies against.
43
+ const flattened = flatten(() => WasmUserValTxData.fromUserValTxCapnp(packed(message)), what)
44
+ const signature = keypair.sign(flattened)
45
+ tx.getSignature().initSome(signature.length)
46
+ tx.getSignature().getSome().copyBuffer(signature)
47
+
48
+ // The opposite of an attestation: the node identifies a validation-layer transaction by
49
+ // the hash of its signature-EXCLUDED flatten (geeq_node_core's UVT wrapper overrides
50
+ // computeRefHash with the no-signature form), and that is the value its uut_status
51
+ // events carry. Taken from the finished message rather than reusing the preimage, so
52
+ // it is computed exactly as the node computes it on receipt.
53
+ const transactionHash = blake3(
54
+ flatten(() => WasmUserValTxData.fromUserValTxCapnp(packed(message)), `signed ${what}`),
55
+ { dkLen: REF_HASH_BYTES },
56
+ )
57
+
58
+ // The fee, though, charges for the flatten WITH the signature: the node's data fee asks
59
+ // for that form whatever the transaction is identified by.
60
+ const signed = flatten(() => WasmUserValTx.fromUserValTxCapnp(packed(message)), `signed ${what}`)
61
+
62
+ return { transaction: toBase64(packed(message)), transactionHash, bytes: signed.length }
63
+ }
64
+
65
+ /**
66
+ * Build and sign a coin transfer. Synchronous; requires a loaded engine.
67
+ *
68
+ * Unlike an attestation this is a **validation layer** transaction: it carries no
69
+ * separate payload, so there is one base64 blob to submit rather than two, and it is
70
+ * posted as type `uvt`.
71
+ *
72
+ * NFTs ride along in the same transaction, with coin: the chain's minimum transfer
73
+ * applies even when the point is the NFTs.
74
+ *
75
+ * Offline — it opens no sockets and reads no ledger. It cannot check that the sender can
76
+ * afford the transfer, that the amount clears the chain's minimum, or that it holds the
77
+ * NFTs; all three need a ledger. {@link GeeqChain.createTransfer} does check them.
78
+ */
79
+ export function createCoinTransferTxSync(input: CoinTransferTxInput): CoinTransferTx {
80
+ assertReady('createCoinTransferTxSync')
81
+
82
+ const { keypair } = input
83
+ assertKeypair(keypair)
84
+
85
+ const chainState = resolveChainState(input)
86
+ const { chainNumber, blockNumber, nodeNumber, nonce } = chainState
87
+ assertChainState(chainState)
88
+
89
+ const receiver = resolveAccountId(input.to as AccountRef)
90
+ if (toBase64(receiver) === toBase64(keypair.accountId)) {
91
+ throw invalidArgument(
92
+ 'to is the sending account. A transfer to yourself moves nothing and still spends ' +
93
+ 'a nonce and a fee.',
94
+ )
95
+ }
96
+
97
+ const pennies = input.amount
98
+ if (typeof pennies !== 'bigint') {
99
+ throw invalidArgument('amount must already be resolved to pennies as a bigint.')
100
+ }
101
+ if (pennies <= 0n) {
102
+ throw invalidArgument(`amount must be greater than zero, received ${pennies} pennies.`)
103
+ }
104
+
105
+ const nfts = input.nfts ?? []
106
+ for (const nft of nfts) {
107
+ if (!(nft.id instanceof Uint8Array) || nft.id.length !== ACCOUNT_ID_BYTES) {
108
+ throw invalidArgument(`An NFT id must be ${ACCOUNT_ID_BYTES} bytes.`)
109
+ }
110
+ assertUint32(nft.position, 'An NFT position')
111
+ }
112
+ if (new Set(nfts.map((nft) => toBase64(nft.id))).size !== nfts.length) {
113
+ throw invalidArgument('The same NFT is listed twice, and the chain refuses a transfer that does that.')
114
+ }
115
+
116
+ const message = new Message()
117
+ const tx = message.initRoot(CapnpUserValTx)
118
+
119
+ const data = tx.initData()
120
+ data.setChainNum(chainNumber)
121
+ data.setBlockNum(blockNumber)
122
+ data.setNodeNum(nodeNumber)
123
+
124
+ const asset = data.initTx().initCoin().initTxType().initAsset()
125
+ asset.setNonce(nonce)
126
+ asset.initAmtCoin().setSome(penniesToUint64(pennies))
127
+ // Both lists must exist even when empty, or the message is not well formed.
128
+ asset.initTokenAssetSubgroups(0)
129
+ const nftList = asset.initNftAssetSubgroups(nfts.length)
130
+ nfts.forEach((nft, index) => {
131
+ const entry = nftList.get(index)
132
+ entry.setPosition(nft.position)
133
+ const item = entry.initItem()
134
+ item.initAcctNumApp(ACCOUNT_ID_BYTES)
135
+ item.getAcctNumApp().copyBuffer(nft.id)
136
+ })
137
+ asset.initAcctNumRcv(ACCOUNT_ID_BYTES)
138
+ asset.getAcctNumRcv().copyBuffer(receiver)
139
+ asset.initPubKey(PUBLIC_KEY_BYTES)
140
+ asset.getPubKey().copyBuffer(keypair.publicKey)
141
+
142
+ const { transaction, transactionHash, bytes } = signValidationTx(message, tx, keypair, 'transfer')
143
+
144
+ return Object.freeze({
145
+ transaction,
146
+ transactionHash,
147
+ publicKey: keypair.publicKey,
148
+ amount: pennies,
149
+ nfts: Object.freeze(nfts.map((nft) => nft.id)),
150
+ to: receiver,
151
+ fee: feeFor(bytes, pennies, chainState.geeqNorm),
152
+ ...pinning(chainState),
153
+ })
154
+ }
155
+
156
+ /** Build and sign a coin transfer, loading the engine if needed. */
157
+ export async function createCoinTransferTx(
158
+ input: CoinTransferTxInput,
159
+ ): Promise<CoinTransferTx> {
160
+ await initGeeq()
161
+ return createCoinTransferTxSync(input)
162
+ }
package/src/types.ts ADDED
@@ -0,0 +1,503 @@
1
+ import type { AccountRef } from './crypto/account-id'
2
+ import type { GeeqHeldNft } from './ledger/coin-accounts'
3
+ import type { GeeqProxyAccount } from './ledger/proxy-accounts'
4
+
5
+ /**
6
+ * Where the WebAssembly engine should be loaded from. Leave it unset and the SDK
7
+ * uses the copy shipped beside its own bundle, which every common bundler emits
8
+ * as a hashed asset automatically.
9
+ */
10
+ export type WasmSource =
11
+ | string
12
+ | URL
13
+ | Request
14
+ | Response
15
+ | ArrayBuffer
16
+ | Uint8Array
17
+ | WebAssembly.Module
18
+
19
+ export interface InitOptions {
20
+ /** Override where `geeq_core_bg.wasm` is fetched from. */
21
+ readonly wasm?: WasmSource
22
+ }
23
+
24
+ /**
25
+ * The kind of ledger record an address points at. Most applications only ever
26
+ * need `'coin-asset'`, which is the default and is what a normal user account is.
27
+ */
28
+ export type GeeqRecordType =
29
+ | 'coin-asset'
30
+ | 'coin-hl'
31
+ | 'coin-micro'
32
+ | 'app-counterparty'
33
+ | 'app-dce'
34
+ | 'app-nft'
35
+ | 'app-stackable'
36
+ | 'proxy-nft-mint'
37
+ | 'proxy-funding'
38
+ | 'proxy-multisig'
39
+ | 'proxy-dce'
40
+ | 'proxy-token-mint'
41
+ | 'gt-nwa'
42
+ | 'param-boolean'
43
+ | 'param-count-2'
44
+ | 'param-count-8'
45
+ | 'sys'
46
+
47
+ /** Which coin family a derivation path belongs to. */
48
+ export type GeeqCoinType = 'mainnet' | 'testnet'
49
+
50
+ export interface WalletAddressOptions {
51
+ /** The chain this address belongs to. Addresses are not portable across chains. */
52
+ readonly chainNumber: number
53
+ /** Defaults to `'coin-asset'` — an ordinary user account. */
54
+ readonly recordType?: GeeqRecordType
55
+ }
56
+
57
+ export interface DecodedWalletAddress {
58
+ /** The address that was decoded, unchanged. */
59
+ readonly address: string
60
+ readonly chainNumber: number
61
+ readonly recordType: GeeqRecordType
62
+ /** The 32-byte account id this address points at. */
63
+ readonly accountId: Uint8Array
64
+ }
65
+
66
+ /** Options for deriving a keypair from a BIP-39 mnemonic along the Geeq path. */
67
+ export interface MnemonicOptions {
68
+ /** Optional BIP-39 passphrase. Defaults to an empty string. */
69
+ readonly passphrase?: string
70
+ /** Defaults to `'coin-asset'`. */
71
+ readonly recordType?: GeeqRecordType
72
+ /** Defaults to `'mainnet'`. */
73
+ readonly coinType?: GeeqCoinType
74
+ /** Defaults to 0. */
75
+ readonly account?: number
76
+ /** Defaults to 0. */
77
+ readonly addressIndex?: number
78
+ }
79
+
80
+ /**
81
+ * An Ed25519 keypair plus the identifiers derived from it.
82
+ *
83
+ * `privateKey` is raw secret material. Never log it, never put it in an error
84
+ * message, and never send it anywhere. Call {@link GeeqKeypair.dispose} when done
85
+ * to release the engine-side copy.
86
+ */
87
+ export interface GeeqKeypair {
88
+ /** 32-byte Ed25519 public key. */
89
+ readonly publicKey: Uint8Array
90
+ /** 32-byte Ed25519 secret key. Secret material. */
91
+ readonly privateKey: Uint8Array
92
+ /** 32-byte account id derived from the public key. */
93
+ readonly accountId: Uint8Array
94
+
95
+ /** The base58 wallet address for this keypair on a given chain. */
96
+ getAddress(options: WalletAddressOptions): string
97
+ /** Sign a message. Throws once {@link GeeqKeypair.dispose} has been called. */
98
+ sign(message: Uint8Array): Uint8Array
99
+ /** Release the engine-side keypair. The byte fields stay readable afterwards. */
100
+ dispose(): void
101
+ }
102
+
103
+ /** The metadata limits this chain publishes in its own ledger. */
104
+ export interface GeeqMetadataLimits {
105
+ /** Maximum NUMBER of metadata entries. From the `metadataVarNum` parameter. */
106
+ readonly maxEntries: number
107
+ /** Maximum bytes in ONE entry. There is no aggregate cap. From `metadataVarBytes`. */
108
+ readonly maxBytesPerEntry: number
109
+ }
110
+
111
+ /**
112
+ * The live chain values a transaction is pinned to.
113
+ *
114
+ * `blockNumber` is the block being *asked for*, which is the ledger's last committed
115
+ * block plus one. Read it from a node with {@link GeeqChain}, or supply it yourself for
116
+ * replay and offline signing.
117
+ */
118
+ export interface ChainStateInput {
119
+ /** The chain this transaction targets — the ledger's own chain number. */
120
+ readonly chainNumber: number
121
+ /** The block it asks to be included in: the ledger's block **+ 1**. */
122
+ readonly blockNumber: number
123
+ /** The receiving node's own number, from the net actor list in the ledger. */
124
+ readonly nodeNumber: number
125
+ /** The signing account's current nonce, from its coin account record. */
126
+ readonly nonce: number
127
+ /** When present, metadata is checked against it before anything is signed. */
128
+ readonly metadataLimits?: GeeqMetadataLimits | undefined
129
+ /**
130
+ * The chain's `geeqNorm` parameter, which every fee is a multiple of. When present, the
131
+ * built transaction carries its {@link GeeqFee}.
132
+ */
133
+ readonly geeqNorm?: bigint | undefined
134
+ /**
135
+ * The node this state describes, and therefore the only node that will accept a
136
+ * transaction built from it — `nodeNumber` is checked with strict equality.
137
+ */
138
+ readonly nodeUrl?: string | undefined
139
+ /**
140
+ * N, the ledger block this was read from. Present only when a chain produced it —
141
+ * there is nothing to report when the caller supplied the numbers.
142
+ */
143
+ readonly ledgerBlockNumber?: number | undefined
144
+ /** `Date.now()` at the read. Present only when a chain produced it. */
145
+ readonly readAt?: number | undefined
146
+ }
147
+
148
+ /**
149
+ * Chain state read from a node, carrying where and when it came from.
150
+ *
151
+ * The same fields as {@link ChainStateInput}, with the provenance ones narrowed from
152
+ * optional to always present — a chain can always say which ledger block and which node
153
+ * it read.
154
+ */
155
+ export interface GeeqChainState extends ChainStateInput {
156
+ /** N — the ledger's last committed block. {@link blockNumber} is this plus one. */
157
+ readonly ledgerBlockNumber: number
158
+ /** `Date.now()` when the ledger was read, so callers can see how stale this is. */
159
+ readonly readAt: number
160
+ /** The node it was read from, and therefore the node it must be submitted to. */
161
+ readonly nodeUrl: string
162
+ /** The signing account's balance, in pennies, as at this read. */
163
+ readonly balance: bigint
164
+ /** The smallest transfer this chain accepts, in pennies, if it publishes one. */
165
+ readonly minimumTransfer: bigint | undefined
166
+ /** The chain's `geeqNorm` parameter, if it publishes one. */
167
+ readonly geeqNorm: bigint | undefined
168
+ }
169
+
170
+ /**
171
+ * What the chain charges for a transaction, in pennies, on top of any amount it sends.
172
+ *
173
+ * Worked out by the SDK the way the node works it out, from the signed bytes, so it is
174
+ * the exact figure rather than an estimate.
175
+ */
176
+ export interface GeeqFee {
177
+ /** The whole fee: {@link data} plus {@link coin}. */
178
+ readonly total: bigint
179
+ /** `geeqNorm` per byte of the signed transaction, and of its payload when it has one. */
180
+ readonly data: bigint
181
+ /**
182
+ * A quarter of a percent of the amount sent, rounded down and capped at
183
+ * `geeqNorm × 10⁷`. Zero for an attestation, which sends no amount.
184
+ */
185
+ readonly coin: bigint
186
+ /** How many bytes {@link data} charges for. */
187
+ readonly bytes: number
188
+ }
189
+
190
+ /** What is being attested, independent of any chain state. */
191
+ export interface AttestationContent {
192
+ /** The account that signs both the attestation and the transaction. */
193
+ readonly keypair: GeeqKeypair
194
+ /** 32-byte hash of the content. The content itself never leaves the device. */
195
+ readonly attestHash: Uint8Array
196
+ /**
197
+ * Optional labels stored alongside the hash, in the order given. Strings are encoded
198
+ * as UTF-8. Anything here is public once submitted.
199
+ */
200
+ readonly metadata?: readonly (string | Uint8Array)[]
201
+ }
202
+
203
+ /**
204
+ * The four chain values supplied explicitly.
205
+ *
206
+ * Kept for replay, deterministic tests and air-gapped signing, where there is no node
207
+ * to ask. Most callers should let {@link GeeqChain} read them instead — none of them can
208
+ * be derived offline, and a wrong one is rejected by the chain.
209
+ */
210
+ export type AttestationTxFlatInput = AttestationContent & {
211
+ readonly chainNumber: number
212
+ readonly blockNumber: number
213
+ readonly nodeNumber: number
214
+ readonly nonce: number
215
+ readonly chainState?: never
216
+ }
217
+
218
+ /** The same four values as one object, which is what a chain read produces. */
219
+ export type AttestationTxStateInput = AttestationContent & {
220
+ readonly chainState: ChainStateInput
221
+ readonly chainNumber?: never
222
+ readonly blockNumber?: never
223
+ readonly nodeNumber?: never
224
+ readonly nonce?: never
225
+ }
226
+
227
+ export type AttestationTxInput = AttestationTxFlatInput | AttestationTxStateInput
228
+
229
+ /**
230
+ * A signed attestation transaction, ready to submit. The SDK does not submit it — post
231
+ * {@link AttestationTx.transaction} and {@link AttestationTx.payload} to the node named
232
+ * by {@link AttestationTx.nodeUrl}, after handing it to `GeeqSocket.track` to hear the
233
+ * verdict.
234
+ */
235
+ export interface AttestationTx {
236
+ /** base64 packed `UserAppTx`. Submit as the `transaction` field. */
237
+ readonly transaction: string
238
+ /** base64 packed `Payload`. Submit as the `payload` field. */
239
+ readonly payload: string
240
+ /** 32-byte BLAKE3 of the flattened payload, as embedded in the transaction. */
241
+ readonly refHashPayload: Uint8Array
242
+ /**
243
+ * 32-byte BLAKE3 of the signed, flattened transaction — how a node identifies it, and
244
+ * the `refHash` its status events carry. `transactionIdOf(tx)` gives it as the base64
245
+ * string a {@link GeeqSocket} matches on.
246
+ */
247
+ readonly transactionHash: Uint8Array
248
+ /** The signer's 32-byte public key — whose status events to subscribe to. */
249
+ readonly publicKey: Uint8Array
250
+ /**
251
+ * What the chain charges the signing account for it. Undefined when the chain state
252
+ * carried no `geeqNorm`.
253
+ */
254
+ readonly fee: GeeqFee | undefined
255
+ /** Exactly what this transaction is pinned to. Frozen. */
256
+ readonly chainState: ChainStateInput
257
+ /**
258
+ * The last block a node takes it at: its target block plus 8. Once the chain's latest
259
+ * block is past this, every node refuses it as too old, and it has to be built and
260
+ * signed again. `GeeqChain.isExpired` checks.
261
+ */
262
+ readonly validUntilBlock: number
263
+ /**
264
+ * Where this must be submitted, when a chain read chose the node.
265
+ *
266
+ * Not a convenience: the node checks that `nodeNumber` is its own, with strict
267
+ * equality, so posting to any other node is a guaranteed rejection. Undefined only
268
+ * when the caller supplied the four values themselves and the SDK never saw a node.
269
+ */
270
+ readonly nodeUrl?: string | undefined
271
+ }
272
+
273
+ /** What is being transferred, independent of any chain state. */
274
+ export interface CoinTransferContent {
275
+ /** The account paying. Must be a coin-asset account on this chain. */
276
+ readonly keypair: GeeqKeypair
277
+ /** Who receives it. Resolved to a 32-byte account id. */
278
+ readonly to: AccountRef
279
+ /**
280
+ * How much, in pennies. Use `toPennies` to get here from a figure. Never
281
+ * zero: the node reads a zero amount as the sender's whole balance.
282
+ */
283
+ readonly amount: bigint
284
+ /** NFTs sent with it. Each must be one the sender holds, named once. */
285
+ readonly nfts?: readonly NftTransferEntry[]
286
+ }
287
+
288
+ /** One NFT a transfer moves. */
289
+ export interface NftTransferEntry {
290
+ /** The NFT's 32-byte id. */
291
+ readonly id: Uint8Array
292
+ /** Where it sits in the sender's list of NFTs, from 0 — as `GeeqCoinAccount.nfts` orders them. */
293
+ readonly position: number
294
+ }
295
+
296
+ /** The four chain values supplied explicitly. For replay and air-gapped signing. */
297
+ export type CoinTransferTxFlatInput = CoinTransferContent & {
298
+ readonly chainNumber: number
299
+ readonly blockNumber: number
300
+ readonly nodeNumber: number
301
+ readonly nonce: number
302
+ readonly chainState?: never
303
+ }
304
+
305
+ /** The same four as one object, which is what a chain read produces. */
306
+ export type CoinTransferTxStateInput = CoinTransferContent & {
307
+ readonly chainState: ChainStateInput
308
+ readonly chainNumber?: never
309
+ readonly blockNumber?: never
310
+ readonly nodeNumber?: never
311
+ readonly nonce?: never
312
+ }
313
+
314
+ export type CoinTransferTxInput = CoinTransferTxFlatInput | CoinTransferTxStateInput
315
+
316
+ /**
317
+ * A signed coin transfer, ready to submit.
318
+ *
319
+ * Note there is no `payload`: a validation-layer transaction is self-contained, unlike an
320
+ * attestation. Post it with `coinTransferSubmitBody`, which sends it as type `uvt`, after
321
+ * handing it to `GeeqSocket.track` to hear the verdict.
322
+ */
323
+ export interface CoinTransferTx {
324
+ /** base64 packed `UserValTx`. Submit as the `transaction` field. */
325
+ readonly transaction: string
326
+ /**
327
+ * 32-byte BLAKE3 of the flattened transaction **without** its signature — how the chain
328
+ * identifies a validation-layer transaction, and the `refHash` its status events carry.
329
+ * Note this is the opposite of an attestation, whose hash includes the signature.
330
+ */
331
+ readonly transactionHash: Uint8Array
332
+ /** The sender's 32-byte public key — whose status events to subscribe to. */
333
+ readonly publicKey: Uint8Array
334
+ /** What was sent, in pennies. */
335
+ readonly amount: bigint
336
+ /** The ids of the NFTs sent with it, if any. */
337
+ readonly nfts: readonly Uint8Array[]
338
+ /**
339
+ * What the chain charges the sender, on top of {@link amount}. Undefined when the chain
340
+ * state carried no `geeqNorm`.
341
+ */
342
+ readonly fee: GeeqFee | undefined
343
+ /** The 32-byte account id it was sent to. */
344
+ readonly to: Uint8Array
345
+ /** Exactly what this transaction is pinned to. Frozen. */
346
+ readonly chainState: ChainStateInput
347
+ /**
348
+ * The last block a node takes it at: its target block plus 8. Once the chain's latest
349
+ * block is past this, every node refuses it as too old, and it has to be built and
350
+ * signed again. `GeeqChain.isExpired` checks.
351
+ */
352
+ readonly validUntilBlock: number
353
+ /** Where it must be submitted, when a chain read chose the node. */
354
+ readonly nodeUrl?: string | undefined
355
+ }
356
+
357
+ /**
358
+ * What a proxy creation needs. The chain values come as one object only — there is no
359
+ * flat form, since a proxy is always built against a ledger read.
360
+ */
361
+ export interface NftMintProxyTxInput {
362
+ /**
363
+ * The account paying for the proxy, which also becomes its admin: the only key that
364
+ * can later mint through it. Must be a coin-asset account on this chain.
365
+ */
366
+ readonly keypair: GeeqKeypair
367
+ /**
368
+ * Which of the admin's proxies this is, from 1. The proxy's id is derived from the
369
+ * admin's public key and this index, as the Geeq wallet derives it, so it must be one
370
+ * the admin has not used — `GeeqChain.findNftMintProxies` gives the next one.
371
+ */
372
+ readonly index: number
373
+ /** Coin moved onto the proxy, in pennies. It pays for the mints made through it. */
374
+ readonly amount: bigint
375
+ readonly chainState: ChainStateInput
376
+ }
377
+
378
+ /**
379
+ * A signed transaction creating an NFT mint proxy, ready to submit.
380
+ *
381
+ * A validation-layer transaction, like a transfer: post it with `coinTransferSubmitBody`,
382
+ * after handing it to `GeeqSocket.track` to hear the verdict.
383
+ */
384
+ export interface NftMintProxyTx {
385
+ /** base64 packed `UserValTx`. Submit as the `transaction` field. */
386
+ readonly transaction: string
387
+ /** 32-byte BLAKE3 of the signature-excluded flatten — the `refHash` its status events carry. */
388
+ readonly transactionHash: Uint8Array
389
+ /** The admin's 32-byte public key — whose status events to subscribe to. */
390
+ readonly publicKey: Uint8Array
391
+ /** The coin moved onto the proxy, in pennies. */
392
+ readonly amount: bigint
393
+ /** Which of the admin's proxies this is, from 1. */
394
+ readonly index: number
395
+ /** The new proxy's 32-byte account id, derived from the admin's key and {@link index}. */
396
+ readonly proxyAccountId: Uint8Array
397
+ /** The new proxy's wallet address on this chain, record type `proxy-nft-mint`. */
398
+ readonly proxyAddress: string
399
+ /**
400
+ * What the chain charges the admin, on top of {@link amount}. Undefined when the chain
401
+ * state carried no `geeqNorm`.
402
+ */
403
+ readonly fee: GeeqFee | undefined
404
+ /** Exactly what this transaction is pinned to. Frozen. */
405
+ readonly chainState: ChainStateInput
406
+ /**
407
+ * The last block a node takes it at: its target block plus 8. Once the chain's latest
408
+ * block is past this, every node refuses it as too old, and it has to be built and
409
+ * signed again. `GeeqChain.isExpired` checks.
410
+ */
411
+ readonly validUntilBlock: number
412
+ /** Where it must be submitted, when a chain read chose the node. */
413
+ readonly nodeUrl?: string | undefined
414
+ }
415
+
416
+ /**
417
+ * What an NFT mint needs. The chain values come as one object only, as for a proxy.
418
+ */
419
+ export interface NftMintTxInput {
420
+ /** The proxy's admin. It signs both the NFT and the transaction. */
421
+ readonly keypair: GeeqKeypair
422
+ /** The 32-byte account id of the proxy minting it, which pays for it. */
423
+ readonly proxyAccountId: Uint8Array
424
+ /** 32-byte hash of what the NFT stands for. The content itself never leaves the device. */
425
+ readonly attestHash: Uint8Array
426
+ /** Optional public labels stored with the NFT, in the order given. */
427
+ readonly metadata?: readonly (string | Uint8Array)[]
428
+ /** Who receives the NFT. Resolved to a 32-byte account id. */
429
+ readonly to: AccountRef
430
+ /** Coin the proxy sends to the receiver along with the NFT, in pennies. */
431
+ readonly amount: bigint
432
+ /** Its `nonce` is the proxy's own, not the admin's. */
433
+ readonly chainState: ChainStateInput
434
+ }
435
+
436
+ /**
437
+ * A signed NFT mint, ready to submit.
438
+ *
439
+ * An application-layer transaction, like an attestation: post `transaction` and `payload`
440
+ * with `attestationSubmitBody`, after handing it to `GeeqSocket.track` to hear the verdict.
441
+ */
442
+ export interface NftMintTx {
443
+ /** base64 packed `UserAppTx`. Submit as the `transaction` field. */
444
+ readonly transaction: string
445
+ /** base64 packed `Payload`. Submit as the `payload` field. */
446
+ readonly payload: string
447
+ /** 32-byte BLAKE3 of the flattened payload, as embedded in the transaction. */
448
+ readonly refHashPayload: Uint8Array
449
+ /** 32-byte BLAKE3 of the signed, flattened transaction — the `refHash` its status events carry. */
450
+ readonly transactionHash: Uint8Array
451
+ /** The admin's 32-byte public key — whose status events to subscribe to. */
452
+ readonly publicKey: Uint8Array
453
+ /** The new NFT's 32-byte id: the hash of its attestation subgroup, as the wallet derives it. */
454
+ readonly nftId: Uint8Array
455
+ /** The proxy minting it. */
456
+ readonly proxyAccountId: Uint8Array
457
+ /** The 32-byte account id receiving it. */
458
+ readonly to: Uint8Array
459
+ /** The coin sent along with it, in pennies. */
460
+ readonly amount: bigint
461
+ /**
462
+ * What the chain charges the proxy — not the admin — on top of {@link amount}. Undefined
463
+ * when the chain state carried no `geeqNorm`.
464
+ */
465
+ readonly fee: GeeqFee | undefined
466
+ /** Exactly what this transaction is pinned to. Frozen. */
467
+ readonly chainState: ChainStateInput
468
+ /**
469
+ * The last block a node takes it at: its target block plus 8. Once the chain's latest
470
+ * block is past this, every node refuses it as too old, and it has to be built and
471
+ * signed again. `GeeqChain.isExpired` checks.
472
+ */
473
+ readonly validUntilBlock: number
474
+ /** Where it must be submitted, when a chain read chose the node. */
475
+ readonly nodeUrl?: string | undefined
476
+ }
477
+
478
+ /** One of an admin's NFT mint proxies, as the ledger holds it. */
479
+ export interface GeeqNftMintProxy extends GeeqProxyAccount {
480
+ /**
481
+ * Which of the admin's proxies it is, from 1. Undefined for a proxy whose id was not
482
+ * derived from the admin's key — one made with an id of its own.
483
+ */
484
+ readonly index: number | undefined
485
+ /** Its wallet address on this chain, record type `proxy-nft-mint`. */
486
+ readonly address: string
487
+ }
488
+
489
+ /** The NFT mint proxies one admin has, and the index its next one takes. */
490
+ export interface GeeqNftMintProxies {
491
+ /** In index order; any without an index come last. */
492
+ readonly proxies: readonly GeeqNftMintProxy[]
493
+ /** One past the highest index in use, or 1 when there is none. */
494
+ readonly nextIndex: number
495
+ }
496
+
497
+ /** An NFT an account holds, with the hash and labels it was minted with. */
498
+ export interface GeeqNft extends GeeqHeldNft {
499
+ /** The hash of what it stands for. Undefined when the application layer ledger has no record of it. */
500
+ readonly attestHash: Uint8Array | undefined
501
+ /** Its public labels as text, in order. */
502
+ readonly metadata: readonly string[]
503
+ }
@@ -0,0 +1,22 @@
1
+ # Geeq WASM engine (vendored)
2
+
3
+ The compiled Geeq core engine and its wasm-bindgen glue, copied verbatim from the `lib/`
4
+ directory of the private `geeq_wasm_lib` repository so the SDK builds and publishes
5
+ without Bitbucket access.
6
+
7
+ | | |
8
+ |---|---|
9
+ | Source | `bitbucket.org/bigkraken5/geeq_wasm_lib`, branch `app15` |
10
+ | Commit | `89159feeaee30affd2d23fdd9998086c94e14c74` |
11
+
12
+ - `browser/` is the build the SDK ships. tsup bundles `geeq_core.js` into both the
13
+ browser and Node outputs, and `scripts/copy-wasm.mjs` puts `geeq_core_bg.wasm` beside them.
14
+ - `nodejs/` is used only by tooling: Jest, which runs as CommonJS and cannot load the ESM
15
+ browser build (see `jest.config.js`), and `example/scripts/browser-check.mjs`. Its
16
+ `package.json` is ours, not upstream's: it marks the folder CommonJS, since the SDK's
17
+ own `package.json` declares `"type": "module"` and Node would otherwise refuse to load it.
18
+
19
+ Do not edit these files by hand. To update, copy `lib/browser` and `lib/nodejs` from the
20
+ new commit over these directories, update the commit above, and run `npm run typecheck`,
21
+ `npm test` and `npm run build && npm run smoke`. The engine must match the protocol
22
+ version of the Cap'n Proto schemas in `src/capnp`.