@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,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="WEB_MODULE" version="4">
3
+ <component name="NewModuleRootManager">
4
+ <content url="file://$MODULE_DIR$">
5
+ <excludeFolder url="file://$MODULE_DIR$/.tmp" />
6
+ <excludeFolder url="file://$MODULE_DIR$/temp" />
7
+ <excludeFolder url="file://$MODULE_DIR$/tmp" />
8
+ </content>
9
+ <orderEntry type="inheritedJdk" />
10
+ <orderEntry type="sourceFolder" forTests="false" />
11
+ </component>
12
+ </module>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/geeq_sdk.iml" filepath="$PROJECT_DIR$/.idea/geeq_sdk.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
package/.idea/vcs.xml ADDED
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="" vcs="Git" />
5
+ </component>
6
+ </project>
package/.nvmrc ADDED
@@ -0,0 +1 @@
1
+ 22
@@ -0,0 +1,20 @@
1
+ # Working on the SDK
2
+
3
+ ```bash
4
+ npm install
5
+ npm test # unit tests, no network needed
6
+ npm run typecheck
7
+ npm run build # builds browser + Node bundles, types, and the engine
8
+ npm run smoke # tests the built package
9
+ ```
10
+
11
+ Everything installs from the public npm registry. The two parts that come from Geeq's
12
+ private repositories are vendored: the WASM engine in [`src/wasm`](./src/wasm/README.md)
13
+ and the Cap'n Proto schemas in [`src/capnp`](./src/capnp/README.md).
14
+
15
+ A full Vue 3 integration example lives in [`example/`](./example). It uses the SDK from
16
+ this folder (`file:..`), so run `npm run build` here before starting it.
17
+
18
+ **Update the README whenever the SDK changes.** It is the package's page on npm, and
19
+ `tests/documented-api.test.ts` checks that its Reference section and Quick start still
20
+ match the code.
@@ -0,0 +1,4 @@
1
+ # How many pennies make one Geeq. The chain counts only pennies and does not publish this,
2
+ # so each app sets it, as the Geeq web app sets VUE_APP_BALANCE_DIVISOR. Use the same value
3
+ # as your other apps and explorer, or the figures will not match theirs.
4
+ VITE_PENNIES_PER_GEEQ=1000000000
@@ -0,0 +1,304 @@
1
+ # Geeq SDK demo — Vue 3
2
+
3
+ A minimal Vue 3 application showing how to use
4
+ [`@geeqdev/geeq-sdk`](..) in a browser app. For how the SDK works and
5
+ how to integrate it, read its
6
+ **[README](../README.md)** — that is the single SDK document. This file
7
+ only covers the demo.
8
+
9
+ To install the demo and try it step by step, see **[USAGE.md](USAGE.md)**.
10
+
11
+ What it does: generates a BIP-39 recovery phrase, derives a Geeq keypair and
12
+ address, finds a node, then submits a document's hash as evidence, sends coin, delegates
13
+ authority to an account that digital assets are created through, creates one, lists the
14
+ account's digital assets and sends them on — and shows what the chain decided about each,
15
+ as the node reports it over its socket. Step 9 looks any transaction
16
+ up in a block explorer by its refHash.
17
+
18
+ The steps build on each other, so each one waits — visible but unusable — until the
19
+ steps before it are done, and says which to do first: the phrase (1), then the keypair
20
+ and its chain number (2), then a node for that chain (3). Steps 4–8 need all three; step
21
+ 9 needs none. The chain number is set only in step 2; step 3 shows it, and moving step 2
22
+ to another chain lets the node go until one is found for the new chain.
23
+
24
+ Keys never leave the tab and nothing is stored — they live in memory and are gone on
25
+ refresh. The only network traffic is to the simulator you name in step 3 and the node it
26
+ points at: the SDK asks the simulator which nodes serve your chain, reads the chosen
27
+ node's ledger to work out what a transaction must be pinned to, opens that node's
28
+ WebSocket to hear verdicts, and the page posts the signed result back. Step 9 also
29
+ calls the block explorer you name there.
30
+
31
+ ## Terminology
32
+
33
+ The demo uses the words the Geeq apps show their users. The SDK and the protocol keep
34
+ their own words, so the two meet wherever the demo calls the SDK:
35
+
36
+ | The demo says | The SDK and the protocol say | SDK calls |
37
+ |---|---|---|
38
+ | **Submit evidence** (step 4): evidence, evidence hash | attestation, attest hash | `createAttestation`, `attestHashOf`, `attestationSubmitBody` |
39
+ | **Delegate authority** (step 6): authority account, delegation | NFT mint proxy, proxy, proxy creation | `createNftMintProxy`, `findNftMintProxies` |
40
+ | **Create a digital asset** (step 7): digital asset | mint an NFT, NFT | `mintNft`, `findNfts`, `createTransfer({ nfts })` |
41
+ | **Send coin** (step 5): coin, transfer | coin asset, unchanged | `createTransfer({ amount })` |
42
+
43
+ The demo's own names use its words too: `EvidencePanel.vue`, `AuthorityPanel.vue`,
44
+ `CreateAssetPanel.vue` and `DigitalAssetsPanel.vue`, and the variables and test ids inside
45
+ them. What comes from the SDK or the chain keeps the protocol's words:
46
+
47
+ - SDK functions and types, such as `AttestationTx`, `NftMintTx` and `GeeqNftMintProxies`,
48
+ and the fields they return, such as `tx.nftId`, `tx.proxyAddress`, `found.proxies` and
49
+ `asset.attestHash`;
50
+ - the record types listed in step 2, such as `coin-asset`, `proxy-nft-mint` and `app-nft`;
51
+ - error messages from the SDK, which the panels show as they come, so one may say "NFT" or
52
+ "proxy";
53
+ - the transaction type the block explorer reports in step 9, such as `attestation`.
54
+
55
+ ## Run it
56
+
57
+ ```bash
58
+ npm install
59
+ npm run dev
60
+ ```
61
+
62
+ The SDK is linked from the repository root (`file:..`), so build it first if you have
63
+ not already:
64
+
65
+ ```bash
66
+ cd .. && npm install && npm run build
67
+ ```
68
+
69
+ ### Pointing it at a local node
70
+
71
+ Real nodes answer on port 2082 behind TLS, with the socket on the same port at `/ws/`, and
72
+ that is what the demo uses by default. For a local node that isn't behind TLS, open
73
+ **Local harness options** in step 3:
74
+
75
+ - **Node port**: the node's HTTP port, used instead of 2082.
76
+ - **Socket URL**: the full socket address, for a node that serves it elsewhere, such as
77
+ `ws://10.0.0.1:3100/ws/`. Leave it empty to derive it from the node.
78
+
79
+ The socket URL is read when the socket opens, so set it before **Find a node**, or press
80
+ Retry after changing it.
81
+
82
+ ## What it demonstrates
83
+
84
+ | Step | SDK functions |
85
+ |---|---|
86
+ | Show engine status | `initGeeq()` |
87
+ | Generate a 12–24 word phrase | `generateMnemonic({ words })` |
88
+ | Validate a pasted phrase as you type | `validateMnemonic()` |
89
+ | Derive the keypair | `keypairFromMnemonic()` |
90
+ | Show the address | `keypair.getAddress({ chainNumber })` |
91
+ | Show public key and account id | `keypair.publicKey`, `keypair.accountId` |
92
+ | Release the engine handle | `keypair.dispose()` |
93
+ | Find a node for a chain number | `GeeqChain.connect({ discovery, chainNumber, penniesPerGeeq })` |
94
+ | Send coin to a pasted address | `chain.createTransfer({ from, to, amount })` |
95
+ | Read and show an amount | `chain.toPennies({ geeq })`, `chain.formatGeeq(pennies)` |
96
+ | Derive chain, block, node and nonce | `chain.getChainState({ account })` |
97
+ | Delegate authority: create an authority account to create digital assets through | `chain.createNftMintProxy({ admin, amount })` |
98
+ | List an account's authority accounts by index, and the next index | `chain.findNftMintProxies({ admin, maxAgeMs: 0 })` |
99
+ | Create a digital asset through a chosen authority account | `chain.mintNft({ admin, index, content: file, metadata })` |
100
+ | List the account's digital assets, with their hashes and labels | `chain.findNfts({ owner, maxAgeMs: 0 })` |
101
+ | Send one digital asset or several to a pasted address | `chain.createTransfer({ from, to, nfts })` |
102
+ | Give back the nonce of a build that was never sent | `chain.discard(tx)` |
103
+ | Check a build that waited is not too old to send | `chain.isExpired(tx)`, `tx.validUntilBlock` |
104
+ | Read a pasted evidence hash | `fromHex(text)` |
105
+ | Hash a chosen file | `attestHashOf(file)` |
106
+ | Check metadata against a chain's limits | `inspectMetadata(labels, limits)` |
107
+ | Build and sign evidence | `chain.createAttestation({ keypair, attestHash })` |
108
+ | Open the node's socket | `GeeqSocket.connect({ endpoint: chain.endpoint })` |
109
+ | Show its connection state | `socket.onConnectionChange(state => …)` |
110
+ | Show every event the node pushes | `socket.onMessage(({ event, payload }) => …)` |
111
+ | Subscribe the account | `socket.subscribe(keypair)` |
112
+ | Listen for a transaction's verdict before sending it | `socket.track(tx)` |
113
+ | Settle a refused submission at once | `tracked.refused({ status, message })` |
114
+ | Follow it to confirmed / rejected / unknown | `tracked.onChange(t => …)`, `t.status`, `t.outcome` |
115
+ | Look a transaction up in a block explorer | `getExplorerTransaction({ explorerUrl, refHash })` |
116
+
117
+ Details worth copying into a real application:
118
+
119
+ **1. Mnemonic generation needs no engine.** `generateMnemonic` and
120
+ `validateMnemonic` are pure JavaScript, so step 1 works instantly while the ~2 MB
121
+ WebAssembly engine is still downloading. Only key derivation waits for it.
122
+
123
+ **2. Mark keypairs raw in reactive state.** A keypair is a class instance holding
124
+ an engine handle and typed arrays. `KeypairPanel.vue` uses `shallowRef` + `markRaw`,
125
+ because deep reactivity would proxy those arrays before they reach WebAssembly and
126
+ buys nothing — the object is replaced wholesale, never mutated.
127
+
128
+ **3. Dispose before replacing.** `keypair.dispose()` releases the engine-side key.
129
+ The demo does this whenever the phrase changes, so repeated derivation does not
130
+ leak handles.
131
+
132
+ **4. Post to `tx.nodeUrl`, not to a URL you kept.** The transaction names the node
133
+ that must process it, and the node compares that number to its own with strict
134
+ equality. The SDK tells you where the chain state came from; submitting anywhere else
135
+ is a guaranteed rejection.
136
+
137
+ **5. One transaction per account per block.** Two sharing a nonce are *both* rejected by
138
+ the chain, so the SDK throws `nonce-collision` on the second rather than building
139
+ something that would destroy the first. It applies to evidence and transfers alike, since
140
+ they share an account's nonce — and to delegations, which spend it too. Creating a digital
141
+ asset spends its authority account's nonce instead, so it is held to one per authority
142
+ account per block. A build that was never sent can give its nonce back with
143
+ `chain.discard(tx)`: step 7 does that when you change the file, labels or authority
144
+ account, so building again is not refused.
145
+
146
+ **6. Amounts name their unit.** `{ geeq: "1.5" }` or `{ pennies }` — never a bare number,
147
+ because a unit mistake in money does not throw. The chain counts only pennies; how many make
148
+ a Geeq is this app's setting, `VITE_PENNIES_PER_GEEQ` in `.env`, passed to `connect` as
149
+ `penniesPerGeeq`. Set it to the divisor your other apps and explorer use. The transfer panel
150
+ shows the penny figure as you type.
151
+
152
+ **7. Track before you submit, and keep the socket for the page's life.** Every panel calls
153
+ `socket.track(tx)` before the POST, because a verdict that arrives before anyone has
154
+ subscribed is lost. `composables/useSocketConnection.ts` opens one socket per chain and
155
+ closes it only when the chain changes; closing it on an account switch would strand every
156
+ pending transaction. The tracked handle lives in the socket, so `TxStatus.vue` keeps
157
+ showing a verdict even after the panel rebuilds its transaction.
158
+
159
+ **8. No socket is not no transactions.** A socket that will not open — most often a
160
+ node certificate the browser has not accepted — does not stop anyone submitting.
161
+ `SocketWarning.vue` warns above Submit that no confirmation will follow, with the
162
+ socket's error and a Retry. If subscribing fails at submit time, the transaction is sent
163
+ anyway and the panel says no confirmation will follow.
164
+
165
+ **9. A `200` is pending, not success.** The panels show a submitted transaction as
166
+ pending until the node's verdict arrives. A refused POST is settled at once with
167
+ `tracked.refused(...)`, since the node never sends a status for a transaction it refused.
168
+
169
+ **10. Every authority account has an index, and the ledger gives the next one.** An
170
+ authority account's id is derived from its admin's key and an index — the first is 1 — as
171
+ the Geeq apps do it. `createNftMintProxy` finds the account's authority accounts and takes
172
+ one past the highest index, so nothing is stored. **Find in ledger** in step 6 lists them
173
+ by index with the balance and nonce that creating a digital asset through them will use,
174
+ and shows the next index. An authority account is listed only once a block has confirmed
175
+ it.
176
+
177
+ **11. A digital asset is sent by its authority account, not by the account.** Step 7 lists
178
+ the account's authority accounts to choose from. The chosen one pays the fee and the
179
+ chain's minimum, which goes to the account with the digital asset, and spends its own
180
+ nonce, so the account can send something else in the same block. The account signs, as
181
+ the authority account's admin. It posts like evidence: `uat`, with a payload, using
182
+ `attestationSubmitBody`.
183
+
184
+ **12. Digital assets are sent as a transfer, with coin.** Step 8 lists the digital assets
185
+ the account holds: which ones from its record in the ledger, and the hash and labels each
186
+ was created with from the application layer ledger. Step 5 sends them: switch it to
187
+ **Digital assets**, get the list, tick one or several. They go in one transfer, posted as
188
+ `uvt`, carrying the chain's minimum transfer, since every transfer must carry at least
189
+ that. A new digital asset is listed once a block has confirmed it, and one sent away is
190
+ gone once a block confirms that.
191
+
192
+ **13. The explorer takes the same id the socket gives you.** Paste the base64 transaction
193
+ id shown under a verdict into step 9. The SDK asks the explorer in hex, the form it
194
+ stores, so you never convert it yourself. `null` means the explorer does not have it yet:
195
+ it only learns of a transaction once a block has decided it.
196
+
197
+ **14. A signed transaction cannot wait long.** A node takes it only until the chain is past
198
+ `tx.validUntilBlock`, its target block plus 8 — about nine blocks after it was built — and
199
+ refuses it with a `400` after that. Every panel shows that block under Submit, and calls
200
+ `chain.isExpired(tx)` before posting, which reads the ledger afresh. If the chain has moved
201
+ past it, nothing is sent: the panel says so and turns Submit off until you build again.
202
+
203
+ ## Vite configuration
204
+
205
+ One line, and it is the only SDK-specific configuration needed:
206
+
207
+ ```ts
208
+ export default defineConfig({
209
+ plugins: [vue()],
210
+ optimizeDeps: { exclude: ['@geeqdev/geeq-sdk'] },
211
+ })
212
+ ```
213
+
214
+ Vite's dependency pre-bundling rewrites the `new URL('...wasm', import.meta.url)`
215
+ the SDK uses to locate its engine. Excluding the SDK keeps that pattern intact, so
216
+ the `.wasm` is emitted as a hashed asset. webpack 5 and Parcel need no equivalent.
217
+
218
+ ## Verifying it
219
+
220
+ ```bash
221
+ npm run typecheck # vue-tsc, strict
222
+ npm run build # type-checks, then builds; emits the .wasm as an asset
223
+ npm run browser-check # drives the production build in real Chromium
224
+ ```
225
+
226
+ `browser-check` serves `dist/` and drives it with Playwright. It also stubs a Geeq
227
+ node — serving a synthetic validation layer ledger built with the same Cap'n Proto
228
+ schema the SDK decodes, and accepting the submitted transaction — so the whole path
229
+ runs in a real browser with no live node. The engine loads over HTTP, a phrase is
230
+ generated, a keypair derived and chain state read. Then evidence and a coin transfer
231
+ are signed, posted and decoded back, to confirm that the block, node, nonce,
232
+ amount and recipient they carry are the ones the ledger and the page supplied. It also
233
+ drives the failure paths: a nonce collision, an unfunded account, an undecodable ledger,
234
+ and amounts that are malformed, below the chain minimum or over the balance.
235
+
236
+ The node's socket is stubbed with Playwright's `routeWebSocket`, which swaps the page's
237
+ `WebSocket` for an in-page stand-in: the SDK's socket code runs unchanged against frames
238
+ the check sends and records, with no network handshake. The check asserts the signer is
239
+ subscribed *before* the POST, then pushes status events keyed by a refHash it computes
240
+ from the posted bytes the way the node does — so an SDK that hashed a transaction
241
+ differently from the node would never see it settle. It drives confirmed evidence, a
242
+ refused submission that settles with no socket event, a transfer that is flagged
243
+ `nonsane` and then found `invalid`, and an arbitrary pushed event. A delegation is posted
244
+ and decoded back too: its authority account must take index 1 with the id the Geeq apps
245
+ derive and name this account as admin, and once confirmed the ledger must list it as
246
+ index 1, so that the next one built takes index 2. A digital asset is then created through
247
+ authority account 1 and decoded back: it must be sent by the authority account on its own
248
+ nonce, to this account, carrying the file's hash and labels. Before it is sent, a label is
249
+ changed and the build redone in the same block on the same nonce; once it is sent, another
250
+ build in that block must be refused. The stub then puts that digital asset and one more on
251
+ the account's record, and serves the application layer ledger with their hashes and
252
+ labels: step 8 must list both, and step 5 sends them — one ticked,
253
+ built, then a second ticked and rebuilt in the same block — as a transfer carrying both, at
254
+ their places in the account's record, with the chain's minimum. Last, a transfer is built and
255
+ the stub's chain moved past its `validUntilBlock`: Submit must say it is too old and post
256
+ nothing.
257
+
258
+ Then the socket's failures:
259
+ - a dropped socket that reconnects and replays its subscriptions;
260
+ - one that cannot come back, where the transaction is sent anyway with a warning — this
261
+ waits out the SDK's 15 s open timeout;
262
+ - one that never opens, where the page warns, still submits, and Retry recovers.
263
+
264
+ Finally it stubs the block explorer's `byRefHash` route, which answers as the real one
265
+ does, including its HTTP 500 for a refHash it does not have. It looks up the evidence by
266
+ its base64 id and checks that the explorer was asked in hex. It also checks that an
267
+ unknown refHash shows as not found, and that a hex refHash is refused before anything is
268
+ sent.
269
+
270
+ It expects Chrome at `/usr/bin/google-chrome`; override with `CHROME_PATH`.
271
+
272
+ ## Layout
273
+
274
+ ```
275
+ src/
276
+ ├── App.vue engine status + page layout
277
+ ├── components/
278
+ │ ├── PhrasePanel.vue generate or restore a recovery phrase
279
+ │ ├── KeypairPanel.vue derive and display the keypair
280
+ │ ├── ChainPanel.vue name a simulator, find a node for step 2's chain; shows its socket
281
+ │ ├── EvidencePanel.vue paste a hash or hash a file, sign, track, submit
282
+ │ ├── TransferPanel.vue send coin or digital assets to a pasted address, track, submit
283
+ │ ├── AuthorityPanel.vue delegate authority, track, submit; list your authority accounts
284
+ │ ├── CreateAssetPanel.vue choose an authority account, create a digital asset through it, track, submit
285
+ │ ├── DigitalAssetsPanel.vue list the digital assets this account holds
286
+ │ ├── ExplorerPanel.vue look a transaction up in a block explorer by refHash
287
+ │ ├── TxStatus.vue one tracked transaction, followed to its verdict
288
+ │ ├── ChainState.vue the chain, block, node and nonce a built transaction is pinned to
289
+ │ ├── TxFee.vue the fee a built transaction will be charged
290
+ │ ├── TxDeadline.vue the last block a built transaction can still be sent at
291
+ │ ├── SocketWarning.vue what a panel says when no verdict can be heard
292
+ │ ├── StepGate.vue holds a step until the steps it builds on are done
293
+ │ └── CopyButton.vue
294
+ ├── composables/
295
+ │ └── useSocketConnection.ts the chain's node socket: opened and closed with the chain
296
+ ├── style.css light/dark, no CSS framework
297
+ └── main.ts
298
+ ```
299
+
300
+ ## Not for real funds
301
+
302
+ This is an SDK demo. It has no persistence, no encryption at rest, no password, and
303
+ it displays a private key on request — all things a real application must handle
304
+ properly. Read it for the SDK calls, not as a security template.
@@ -0,0 +1,74 @@
1
+ # Using the demo
2
+
3
+ How to install the demo and try each step. For how the code works, see the
4
+ [README](README.md).
5
+
6
+ ## Install
7
+
8
+ You need Node.js 20 or newer. The demo lives in the SDK repository's `example/` folder
9
+ and uses the SDK from the repository root, so build that first:
10
+
11
+ ```bash
12
+ cd .. && npm install && npm run build
13
+ cd example && npm install
14
+ cp .env.example .env
15
+ ```
16
+
17
+ In `.env`, check `VITE_PENNIES_PER_GEEQ` (how many pennies make one Geeq). Use the same
18
+ value as your other Geeq apps and your explorer.
19
+
20
+ ## Run
21
+
22
+ ```bash
23
+ npm run dev
24
+ ```
25
+
26
+ Open the printed URL and wait for **Engine ready**.
27
+
28
+ ## Steps
29
+
30
+ Steps unlock in order. Steps 1–3 set up the account and the node, steps 4–8 need them,
31
+ and step 9 works on its own. Nothing is saved, so a refresh clears your keys.
32
+
33
+ 1. **Recovery phrase**: click **Create new**, then **Generate phrase**. Or click
34
+ **Restore existing** and paste your own.
35
+ 2. **Keypair & address**: set the **Chain number** and click **Derive keypair**.
36
+ 3. **Chain**: paste a **Simulator URL** and click **Find a node**.
37
+ 4. **Submit evidence**: paste a hash or choose a file.
38
+ 5. **Send coin or digital assets**: choose **Geeq** or **Digital assets** and paste the
39
+ recipient's address.
40
+ 6. **Delegate authority**: create and fund an authority account. You need one before you
41
+ can create a digital asset.
42
+ 7. **Create a digital asset**: click **Find my authority accounts**, pick one, and choose
43
+ a file.
44
+ 8. **Your digital assets**: click **Get my digital assets**.
45
+ 9. **Block explorer**: paste an explorer URL and a transaction id from steps 4–7.
46
+
47
+ In steps 4–7, click **Build & sign**, then **Submit to node**, and wait for the
48
+ **Verdict**. A new account is empty, so send it some coin before step 4.
49
+
50
+ ## Terminology
51
+
52
+ The demo uses the words the Geeq apps show their users. The SDK and the protocol use
53
+ their own:
54
+
55
+ | In the demo | In the SDK and the protocol |
56
+ | ------------------------------------- | -------------------------------- |
57
+ | Submit evidence, evidence | attestation |
58
+ | Delegate authority, authority account | create an NFT mint proxy, proxy |
59
+ | Create a digital asset, digital asset | mint an NFT, NFT |
60
+ | Send coin | coin asset, unchanged |
61
+
62
+ So an error message from the SDK may say "NFT" or "proxy", the record types in step 2
63
+ keep their protocol names, and the block explorer in step 9 reports its own transaction
64
+ types, such as `attestation`. The [README](README.md#terminology) lists which names in the
65
+ code changed.
66
+
67
+ ## If something goes wrong
68
+
69
+ | You see | Fix |
70
+ | ------------------------------------------------------ | ------------------------------------------------------------------------------------- |
71
+ | **Engine failed to load** | Rebuild the SDK and restart `npm run dev`. |
72
+ | `Cannot convert undefined to a BigInt` | Set `VITE_PENNIES_PER_GEEQ` in `.env` (VITE_PENNIES_PER_GEEQ=1000000000) and restart. |
73
+ | **Could not reach that simulator**, or socket `failed` | Open the URL in a tab, accept its certificate, then try again. |
74
+ | **This account has never received coins** | Send coin to the account's address first. |
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Geeq SDK Demo</title>
7
+ </head>
8
+ <body>
9
+ <div id="app"></div>
10
+ <script type="module" src="/src/main.ts"></script>
11
+ </body>
12
+ </html>