@geeqdev/geeq-sdk 0.1.0 → 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.
- package/.idea/geeq_sdk.iml +12 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/.nvmrc +1 -0
- package/CONTRIBUTING.md +20 -0
- package/example/.env.example +4 -0
- package/example/README.md +304 -0
- package/example/USAGE.md +74 -0
- package/example/index.html +12 -0
- package/example/package-lock.json +1557 -0
- package/example/package.json +25 -0
- package/example/scripts/browser-check.mjs +1421 -0
- package/example/scripts/match-derivation.mjs +65 -0
- package/example/src/App.vue +138 -0
- package/example/src/components/AuthorityPanel.vue +277 -0
- package/example/src/components/ChainPanel.vue +267 -0
- package/example/src/components/ChainState.vue +59 -0
- package/example/src/components/CopyButton.vue +25 -0
- package/example/src/components/CreateAssetPanel.vue +313 -0
- package/example/src/components/DigitalAssetsPanel.vue +68 -0
- package/example/src/components/EvidencePanel.vue +396 -0
- package/example/src/components/ExplorerPanel.vue +119 -0
- package/example/src/components/KeypairPanel.vue +182 -0
- package/example/src/components/PhrasePanel.vue +105 -0
- package/example/src/components/SocketWarning.vue +18 -0
- package/example/src/components/StepGate.vue +24 -0
- package/example/src/components/TransferPanel.vue +416 -0
- package/example/src/components/TxDeadline.vue +19 -0
- package/example/src/components/TxFee.vue +31 -0
- package/example/src/components/TxStatus.vue +105 -0
- package/example/src/composables/useSocketConnection.ts +80 -0
- package/example/src/env.d.ts +12 -0
- package/example/src/main.ts +5 -0
- package/example/src/pennies.ts +7 -0
- package/example/src/style.css +149 -0
- package/example/tsconfig.json +19 -0
- package/example/vite.config.ts +26 -0
- package/jest.config.js +18 -0
- package/package.json +1 -5
- package/scripts/copy-wasm.mjs +21 -0
- package/scripts/smoke.mjs +382 -0
- package/src/address/record-type.ts +77 -0
- package/src/address/wallet-address.ts +105 -0
- package/src/amount.ts +143 -0
- package/src/assert.ts +30 -0
- package/src/capnp/README.md +15 -0
- package/src/capnp/adminNetTx.capnp.ts +66 -0
- package/src/capnp/appLayBlock.capnp.ts +26 -0
- package/src/capnp/appLayLedger.capnp.ts +35 -0
- package/src/capnp/appLayRec.capnp.ts +159 -0
- package/src/capnp/chainStateView.capnp.ts +49 -0
- package/src/capnp/coinAcctRec.capnp.ts +193 -0
- package/src/capnp/genesisBlock.capnp.ts +56 -0
- package/src/capnp/hubContentStmt.capnp.ts +59 -0
- package/src/capnp/misc.capnp.ts +19 -0
- package/src/capnp/netActorRec.capnp.ts +64 -0
- package/src/capnp/nodeContentStmt.capnp.ts +62 -0
- package/src/capnp/nodePayloadList.capnp.ts +28 -0
- package/src/capnp/nodeTxList.capnp.ts +37 -0
- package/src/capnp/paramAcctRec.capnp.ts +669 -0
- package/src/capnp/payload.capnp.ts +261 -0
- package/src/capnp/proxyAcctRec.capnp.ts +259 -0
- package/src/capnp/relayContentStmt.capnp.ts +85 -0
- package/src/capnp/sysAcctRec.capnp.ts +47 -0
- package/src/capnp/userAppTx.capnp.ts +269 -0
- package/src/capnp/userValTx.capnp.ts +1999 -0
- package/src/capnp/valLayBlock.capnp.ts +93 -0
- package/src/capnp/valLayLedger.capnp.ts +80 -0
- package/src/chain/geeq-chain.ts +1094 -0
- package/src/chain/geeq-node.ts +105 -0
- package/src/chain/nonce-guard.ts +73 -0
- package/src/chain/select.ts +149 -0
- package/src/crypto/account-id.ts +83 -0
- package/src/crypto/keypair.ts +221 -0
- package/src/crypto/mnemonic.ts +69 -0
- package/src/encoding/base64.ts +58 -0
- package/src/encoding/hex.ts +42 -0
- package/src/errors.ts +122 -0
- package/src/index.ts +195 -0
- package/src/ledger/coin-accounts.ts +73 -0
- package/src/ledger/decode.ts +147 -0
- package/src/ledger/geeq-ledger.ts +184 -0
- package/src/ledger/net-actors.ts +129 -0
- package/src/ledger/nft-records.ts +49 -0
- package/src/ledger/params.ts +82 -0
- package/src/ledger/proxy-accounts.ts +51 -0
- package/src/net/discovery.ts +231 -0
- package/src/net/explorer.ts +123 -0
- package/src/net/read-ledger.ts +58 -0
- package/src/net/routes.ts +79 -0
- package/src/net/transport.ts +221 -0
- package/src/net/websocket.ts +94 -0
- package/src/runtime/bindings.ts +38 -0
- package/src/runtime/init.ts +49 -0
- package/src/runtime/wasm-loader.browser.ts +22 -0
- package/src/runtime/wasm-loader.node.ts +16 -0
- package/src/socket/geeq-socket.ts +827 -0
- package/src/socket/tracked-tx.ts +319 -0
- package/src/socket/tx-status.ts +131 -0
- package/src/tx/attestation.ts +222 -0
- package/src/tx/content.ts +82 -0
- package/src/tx/encode.ts +107 -0
- package/src/tx/expiry.ts +11 -0
- package/src/tx/fee.ts +31 -0
- package/src/tx/metadata.ts +100 -0
- package/src/tx/nft.ts +108 -0
- package/src/tx/proxy.ts +156 -0
- package/src/tx/transfer.ts +162 -0
- package/src/types.ts +503 -0
- package/src/wasm/README.md +22 -0
- package/src/wasm/browser/geeq_core.d.ts +1224 -0
- package/src/wasm/browser/geeq_core.js +6014 -0
- package/src/wasm/browser/geeq_core_bg.wasm +0 -0
- package/src/wasm/browser/geeq_core_bg.wasm.d.ts +447 -0
- package/src/wasm/nodejs/geeq_core.d.ts +753 -0
- package/src/wasm/nodejs/geeq_core.js +6016 -0
- package/src/wasm/nodejs/geeq_core_bg.wasm +0 -0
- package/src/wasm/nodejs/geeq_core_bg.wasm.d.ts +447 -0
- package/src/wasm/nodejs/package.json +3 -0
- package/tsconfig.build.json +8 -0
- package/tsconfig.json +20 -0
- package/tsup.config.ts +60 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { generateMnemonic as scureGenerate, validateMnemonic as scureValidate } from '@scure/bip39'
|
|
2
|
+
import { wordlist } from '@scure/bip39/wordlists/english'
|
|
3
|
+
import { invalidArgument } from '../errors'
|
|
4
|
+
|
|
5
|
+
/** Supported mnemonic lengths. 12 words is the common default; 24 is the strongest. */
|
|
6
|
+
export type MnemonicWordCount = 12 | 15 | 18 | 21 | 24
|
|
7
|
+
|
|
8
|
+
const ENTROPY_BITS: Readonly<Record<MnemonicWordCount, number>> = {
|
|
9
|
+
12: 128,
|
|
10
|
+
15: 160,
|
|
11
|
+
18: 192,
|
|
12
|
+
21: 224,
|
|
13
|
+
24: 256,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const MNEMONIC_WORD_COUNTS = Object.freeze([12, 15, 18, 21, 24] as const)
|
|
17
|
+
|
|
18
|
+
export interface GenerateMnemonicOptions {
|
|
19
|
+
/** How many words to generate. Defaults to 12. */
|
|
20
|
+
readonly words?: MnemonicWordCount
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Generate a fresh BIP-39 recovery phrase, using the platform's cryptographically
|
|
25
|
+
* secure random source.
|
|
26
|
+
*
|
|
27
|
+
* Pure JavaScript — no engine needed, so this works before {@link initGeeq}
|
|
28
|
+
* resolves. Phrases are generated from the English wordlist, which is what
|
|
29
|
+
* {@link keypairFromMnemonic} expects.
|
|
30
|
+
*
|
|
31
|
+
* The phrase is the *only* way to recover the wallet. Show it to the user once,
|
|
32
|
+
* let them write it down, and never transmit or persist it in plaintext.
|
|
33
|
+
*
|
|
34
|
+
* ```ts
|
|
35
|
+
* const mnemonic = generateMnemonic() // 12 words
|
|
36
|
+
* const long = generateMnemonic({ words: 24 }) // 24 words
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export function generateMnemonic(options: GenerateMnemonicOptions = {}): string {
|
|
40
|
+
const words = options.words ?? 12
|
|
41
|
+
const bits = ENTROPY_BITS[words]
|
|
42
|
+
if (bits === undefined) {
|
|
43
|
+
throw invalidArgument(
|
|
44
|
+
`words must be one of ${MNEMONIC_WORD_COUNTS.join(', ')}, received ${String(words)}.`,
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
return scureGenerate(wordlist, bits)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Check a recovery phrase for the right word count, known words, and a valid
|
|
52
|
+
* checksum. Use it to give immediate feedback on a restore form rather than
|
|
53
|
+
* waiting for {@link keypairFromMnemonic} to throw.
|
|
54
|
+
*
|
|
55
|
+
* Pure JavaScript — no engine needed.
|
|
56
|
+
*/
|
|
57
|
+
export function validateMnemonic(mnemonic: string): boolean {
|
|
58
|
+
if (typeof mnemonic !== 'string') return false
|
|
59
|
+
try {
|
|
60
|
+
return scureValidate(normalizeMnemonic(mnemonic), wordlist)
|
|
61
|
+
} catch {
|
|
62
|
+
return false
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Trim and collapse whitespace so pasted phrases validate as typed ones do. */
|
|
67
|
+
export function normalizeMnemonic(mnemonic: string): string {
|
|
68
|
+
return mnemonic.trim().replace(/\s+/gu, ' ').toLowerCase()
|
|
69
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { invalidArgument } from '../errors'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Base64 in both directions, with one behaviour on every runtime.
|
|
5
|
+
*
|
|
6
|
+
* The platform primitives disagree on bad input: `Buffer.from(s, 'base64')` silently
|
|
7
|
+
* drops characters it does not recognise and returns a short buffer, while `atob`
|
|
8
|
+
* throws. Decoding a ledger through the first would turn "this URL is not a Geeq node"
|
|
9
|
+
* into "this ledger is corrupt" — so the input is validated here and both paths take
|
|
10
|
+
* the same branch.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** Standard alphabet, correct padding, whole 4-character groups. */
|
|
14
|
+
const BASE64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/
|
|
15
|
+
|
|
16
|
+
export function toBase64(bytes: Uint8Array): string {
|
|
17
|
+
if (typeof btoa === 'function') {
|
|
18
|
+
let binary = ''
|
|
19
|
+
for (const byte of bytes) binary += String.fromCharCode(byte)
|
|
20
|
+
return btoa(binary)
|
|
21
|
+
}
|
|
22
|
+
return Buffer.from(bytes).toString('base64')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Decode base64, rejecting anything the alphabet does not cover.
|
|
27
|
+
*
|
|
28
|
+
* `label` names the thing being decoded so the message points at the caller's data
|
|
29
|
+
* rather than at this function.
|
|
30
|
+
*/
|
|
31
|
+
export function fromBase64(value: string, label = 'value'): Uint8Array {
|
|
32
|
+
if (typeof value !== 'string') {
|
|
33
|
+
throw invalidArgument(`${label} must be a base64 string, received ${typeof value}.`)
|
|
34
|
+
}
|
|
35
|
+
if (value === '') return new Uint8Array(0)
|
|
36
|
+
|
|
37
|
+
if (!BASE64.test(value)) {
|
|
38
|
+
// Naming the offending character and its index is the difference between "this is
|
|
39
|
+
// not base64" and "your proxy returned an HTML login page".
|
|
40
|
+
const index = value.search(/[^A-Za-z0-9+/=]/u)
|
|
41
|
+
const detail =
|
|
42
|
+
index === -1
|
|
43
|
+
? `its length (${value.length}) is not a multiple of 4, or its padding is misplaced`
|
|
44
|
+
: `it contains ${JSON.stringify(value[index])} at index ${index}`
|
|
45
|
+
throw invalidArgument(
|
|
46
|
+
`${label} is not valid base64: ${detail}. ` +
|
|
47
|
+
'A value that looks like markup or JSON usually means the URL is not a Geeq node.',
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (typeof atob === 'function') {
|
|
52
|
+
const binary = atob(value)
|
|
53
|
+
const bytes = new Uint8Array(binary.length)
|
|
54
|
+
for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i)
|
|
55
|
+
return bytes
|
|
56
|
+
}
|
|
57
|
+
return new Uint8Array(Buffer.from(value, 'base64'))
|
|
58
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { invalidArgument } from '../errors'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Hex in both directions.
|
|
5
|
+
*
|
|
6
|
+
* Every hash this SDK returns is bytes, and almost every application immediately turns
|
|
7
|
+
* them into hex to show or log. Doing that by hand is three lines everyone writes
|
|
8
|
+
* slightly differently — and `toString(16)` without padding silently drops a leading
|
|
9
|
+
* zero, producing a 63-character "32-byte" hash that compares unequal to itself.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const HEX = /^[0-9a-fA-F]*$/
|
|
13
|
+
|
|
14
|
+
export function toHex(bytes: Uint8Array): string {
|
|
15
|
+
let hex = ''
|
|
16
|
+
for (const byte of bytes) hex += byte.toString(16).padStart(2, '0')
|
|
17
|
+
return hex
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function fromHex(value: string, label = 'value'): Uint8Array {
|
|
21
|
+
if (typeof value !== 'string') {
|
|
22
|
+
throw invalidArgument(`${label} must be a hex string, received ${typeof value}.`)
|
|
23
|
+
}
|
|
24
|
+
const text = value.startsWith('0x') || value.startsWith('0X') ? value.slice(2) : value
|
|
25
|
+
if (text.length % 2 !== 0) {
|
|
26
|
+
throw invalidArgument(
|
|
27
|
+
`${label} must have an even number of hex digits, received ${text.length}.`,
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
if (!HEX.test(text)) {
|
|
31
|
+
const index = text.search(/[^0-9a-fA-F]/u)
|
|
32
|
+
throw invalidArgument(
|
|
33
|
+
`${label} is not valid hex: it contains ${JSON.stringify(text[index])} at index ${index}.`,
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const bytes = new Uint8Array(text.length / 2)
|
|
38
|
+
for (let i = 0; i < bytes.length; i += 1) {
|
|
39
|
+
bytes[i] = Number.parseInt(text.slice(i * 2, i * 2 + 2), 16)
|
|
40
|
+
}
|
|
41
|
+
return bytes
|
|
42
|
+
}
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every error thrown by this SDK is a {@link GeeqError} carrying a stable
|
|
3
|
+
* {@link GeeqErrorCode}. Branch on `code`, never on the message text.
|
|
4
|
+
*/
|
|
5
|
+
export type GeeqErrorCode =
|
|
6
|
+
/** An SDK function was called before the engine finished loading. */
|
|
7
|
+
| 'not-initialized'
|
|
8
|
+
/** The WebAssembly engine could not be loaded. */
|
|
9
|
+
| 'init-failed'
|
|
10
|
+
/** A public or private key was the wrong length, or the pair did not match. */
|
|
11
|
+
| 'invalid-key'
|
|
12
|
+
/** A wallet address was malformed, or encoded a record type we do not know. */
|
|
13
|
+
| 'invalid-address'
|
|
14
|
+
/** A caller-supplied argument was outside its allowed range. */
|
|
15
|
+
| 'invalid-argument'
|
|
16
|
+
/** A node could not be reached at all — no HTTP response arrived. */
|
|
17
|
+
| 'network-unreachable'
|
|
18
|
+
/** A node, simulator or explorer answered with an error status. See `status` and `nodeDetails`. */
|
|
19
|
+
| 'node-error'
|
|
20
|
+
/** A node answered, but its ledger could not be decoded. */
|
|
21
|
+
| 'ledger-unreadable'
|
|
22
|
+
/** The node's chain is not the one the caller said to expect. */
|
|
23
|
+
| 'chain-mismatch'
|
|
24
|
+
/** The signing account has no record on this chain yet — it needs funding. */
|
|
25
|
+
| 'account-not-found'
|
|
26
|
+
/** The account exists but its record type cannot sign this transaction. */
|
|
27
|
+
| 'account-not-attestable'
|
|
28
|
+
/**
|
|
29
|
+
* The node being targeted cannot be identified, or cannot process transactions. Also
|
|
30
|
+
* thrown when a simulator or explorer answers with something it should not serve.
|
|
31
|
+
*/
|
|
32
|
+
| 'node-unusable'
|
|
33
|
+
/** Metadata exceeds a limit this chain publishes in its ledger. */
|
|
34
|
+
| 'metadata-too-large'
|
|
35
|
+
/** A second transaction was built for one account against one block. */
|
|
36
|
+
| 'nonce-collision'
|
|
37
|
+
/** The sending account does not hold enough coin. */
|
|
38
|
+
| 'insufficient-funds'
|
|
39
|
+
/** The amount is below the smallest transfer this chain accepts. */
|
|
40
|
+
| 'amount-below-minimum'
|
|
41
|
+
/** The sending account does not hold an NFT it was asked to send. */
|
|
42
|
+
| 'nft-not-owned'
|
|
43
|
+
/** A node's WebSocket could not be opened, or this runtime has no WebSocket at all. */
|
|
44
|
+
| 'socket-unreachable'
|
|
45
|
+
/** The {@link GeeqSocket} was disposed, so it will not send or receive again. */
|
|
46
|
+
| 'socket-closed'
|
|
47
|
+
|
|
48
|
+
export interface GeeqErrorOptions {
|
|
49
|
+
readonly cause?: unknown
|
|
50
|
+
/** HTTP status, when a server answered. */
|
|
51
|
+
readonly status?: number
|
|
52
|
+
/**
|
|
53
|
+
* The server's own `details`, verbatim; a list of them is joined with `; `. Never parse
|
|
54
|
+
* it — branch on `code`.
|
|
55
|
+
*/
|
|
56
|
+
readonly nodeDetails?: string
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export class GeeqError extends Error {
|
|
60
|
+
readonly code: GeeqErrorCode
|
|
61
|
+
readonly status?: number | undefined
|
|
62
|
+
readonly nodeDetails?: string | undefined
|
|
63
|
+
|
|
64
|
+
constructor(code: GeeqErrorCode, message: string, options?: GeeqErrorOptions) {
|
|
65
|
+
// Only `cause` belongs to Error; the rest are ours. exactOptionalPropertyTypes
|
|
66
|
+
// forbids handing it an explicit undefined, hence the ternary.
|
|
67
|
+
super(message, options?.cause === undefined ? undefined : { cause: options.cause })
|
|
68
|
+
this.name = 'GeeqError'
|
|
69
|
+
this.code = code
|
|
70
|
+
this.status = options?.status
|
|
71
|
+
this.nodeDetails = options?.nodeDetails
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function make(code: GeeqErrorCode) {
|
|
76
|
+
return (message: string, cause?: unknown): GeeqError =>
|
|
77
|
+
new GeeqError(code, message, cause === undefined ? undefined : { cause })
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function notInitialized(what: string): GeeqError {
|
|
81
|
+
return new GeeqError(
|
|
82
|
+
'not-initialized',
|
|
83
|
+
`${what} was called before the Geeq engine finished loading. ` +
|
|
84
|
+
'Await initGeeq() once at startup, or use the async helpers (generateKeypair, ' +
|
|
85
|
+
'importKeypair, createWalletAddress) which load it for you.',
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** The default chain was used before one was installed. */
|
|
90
|
+
export function noChain(what: string): GeeqError {
|
|
91
|
+
return new GeeqError(
|
|
92
|
+
'not-initialized',
|
|
93
|
+
`${what} was called before a chain was initialised. ` +
|
|
94
|
+
'Await GeeqChain.init({ endpoint }) once at startup, or pass an explicit chain ' +
|
|
95
|
+
'from GeeqChain.connect({ endpoint }).',
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const invalidKey = make('invalid-key')
|
|
100
|
+
export const invalidAddress = make('invalid-address')
|
|
101
|
+
export const invalidArgument = make('invalid-argument')
|
|
102
|
+
export const ledgerUnreadable = make('ledger-unreadable')
|
|
103
|
+
export const chainMismatch = make('chain-mismatch')
|
|
104
|
+
export const accountNotFound = make('account-not-found')
|
|
105
|
+
export const accountNotAttestable = make('account-not-attestable')
|
|
106
|
+
export const nodeUnusable = make('node-unusable')
|
|
107
|
+
export const metadataTooLarge = make('metadata-too-large')
|
|
108
|
+
export const nonceCollision = make('nonce-collision')
|
|
109
|
+
export const insufficientFunds = make('insufficient-funds')
|
|
110
|
+
export const amountBelowMinimum = make('amount-below-minimum')
|
|
111
|
+
export const nftNotOwned = make('nft-not-owned')
|
|
112
|
+
export const socketUnreachable = make('socket-unreachable')
|
|
113
|
+
export const socketClosed = make('socket-closed')
|
|
114
|
+
export const networkUnreachable = make('network-unreachable')
|
|
115
|
+
|
|
116
|
+
/** A node answered with an error status. `details` is the node's own message. */
|
|
117
|
+
export function nodeError(message: string, status: number, details?: string): GeeqError {
|
|
118
|
+
return new GeeqError('node-error', message, {
|
|
119
|
+
status,
|
|
120
|
+
...(details === undefined ? {} : { nodeDetails: details }),
|
|
121
|
+
})
|
|
122
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Geeq SDK — browser-first TypeScript access to the Geeq blockchain.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* import { generateKeypair } from '@geeqdev/geeq-sdk'
|
|
6
|
+
*
|
|
7
|
+
* const keypair = await generateKeypair()
|
|
8
|
+
* const address = keypair.getAddress({ chainNumber: 1 })
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// Engine lifecycle
|
|
13
|
+
export { initGeeq, isGeeqReady } from './runtime/init'
|
|
14
|
+
|
|
15
|
+
// Keypairs
|
|
16
|
+
export {
|
|
17
|
+
generateKeypair,
|
|
18
|
+
generateKeypairSync,
|
|
19
|
+
importKeypair,
|
|
20
|
+
importKeypairSync,
|
|
21
|
+
keypairFromSecretKey,
|
|
22
|
+
keypairFromSecretKeySync,
|
|
23
|
+
keypairFromMnemonic,
|
|
24
|
+
keypairFromMnemonicSync,
|
|
25
|
+
verifySignature,
|
|
26
|
+
} from './crypto/keypair'
|
|
27
|
+
|
|
28
|
+
export { accountIdFromPublicKey, ACCOUNT_ID_BYTES, PUBLIC_KEY_BYTES } from './crypto/account-id'
|
|
29
|
+
export type { AccountRef } from './crypto/account-id'
|
|
30
|
+
|
|
31
|
+
// Recovery phrases
|
|
32
|
+
export {
|
|
33
|
+
generateMnemonic,
|
|
34
|
+
validateMnemonic,
|
|
35
|
+
normalizeMnemonic,
|
|
36
|
+
MNEMONIC_WORD_COUNTS,
|
|
37
|
+
} from './crypto/mnemonic'
|
|
38
|
+
export type { GenerateMnemonicOptions, MnemonicWordCount } from './crypto/mnemonic'
|
|
39
|
+
|
|
40
|
+
// Wallet addresses
|
|
41
|
+
export {
|
|
42
|
+
createWalletAddress,
|
|
43
|
+
createWalletAddressSync,
|
|
44
|
+
decodeWalletAddress,
|
|
45
|
+
decodeWalletAddressSync,
|
|
46
|
+
isValidWalletAddress,
|
|
47
|
+
} from './address/wallet-address'
|
|
48
|
+
|
|
49
|
+
export { GEEQ_RECORD_TYPES, DEFAULT_RECORD_TYPE } from './address/record-type'
|
|
50
|
+
|
|
51
|
+
// Chains and nodes
|
|
52
|
+
export { GeeqChain, attest, initChain, transfer } from './chain/geeq-chain'
|
|
53
|
+
export { GeeqNode } from './chain/geeq-node'
|
|
54
|
+
export { GEEQ_NODE_PORT, selectNode } from './chain/select'
|
|
55
|
+
export { discover, listChains, listNodes } from './net/discovery'
|
|
56
|
+
export { defaultTransport } from './net/transport'
|
|
57
|
+
export {
|
|
58
|
+
attestationSubmitBody,
|
|
59
|
+
coinTransferSubmitBody,
|
|
60
|
+
ledgerRoute,
|
|
61
|
+
socketRoute,
|
|
62
|
+
submitRoute,
|
|
63
|
+
} from './net/routes'
|
|
64
|
+
export type {
|
|
65
|
+
AttestationRequest,
|
|
66
|
+
ChainStateRequest,
|
|
67
|
+
CoinTransferRequest,
|
|
68
|
+
GeeqChainOptions,
|
|
69
|
+
NftMintRequest,
|
|
70
|
+
NftMintProxiesRequest,
|
|
71
|
+
NftMintProxyRequest,
|
|
72
|
+
NftsRequest,
|
|
73
|
+
} from './chain/geeq-chain'
|
|
74
|
+
export type {
|
|
75
|
+
Discovery,
|
|
76
|
+
GeeqDiscoveredChain,
|
|
77
|
+
GeeqDiscoveredNode,
|
|
78
|
+
GeeqNodeKind,
|
|
79
|
+
} from './net/discovery'
|
|
80
|
+
export type {
|
|
81
|
+
GeeqHttpResponse,
|
|
82
|
+
GeeqRequestInit,
|
|
83
|
+
GeeqTransport,
|
|
84
|
+
} from './net/transport'
|
|
85
|
+
|
|
86
|
+
// Block explorer — look a transaction up after a block has decided it
|
|
87
|
+
export { getExplorerTransaction } from './net/explorer'
|
|
88
|
+
export type { ExplorerTransactionRequest, GeeqExplorerTransaction } from './net/explorer'
|
|
89
|
+
|
|
90
|
+
// Sockets — what happened to the transactions you sent, and anything else a node pushes
|
|
91
|
+
export { GeeqSocket } from './socket/geeq-socket'
|
|
92
|
+
export { GeeqTrackedTx } from './socket/tracked-tx'
|
|
93
|
+
export { transactionIdOf } from './socket/tx-status'
|
|
94
|
+
export { defaultWebSocketFactory } from './net/websocket'
|
|
95
|
+
export type {
|
|
96
|
+
GeeqListenOptions,
|
|
97
|
+
GeeqSendOptions,
|
|
98
|
+
GeeqSocketMessage,
|
|
99
|
+
GeeqSocketOptions,
|
|
100
|
+
GeeqSocketState,
|
|
101
|
+
GeeqTrackable,
|
|
102
|
+
GeeqTrackOptions,
|
|
103
|
+
} from './socket/geeq-socket'
|
|
104
|
+
export type {
|
|
105
|
+
GeeqTxConfirmed,
|
|
106
|
+
GeeqTxOutcome,
|
|
107
|
+
GeeqTxRefusal,
|
|
108
|
+
GeeqTxRejected,
|
|
109
|
+
GeeqTxRejectionReason,
|
|
110
|
+
GeeqTxState,
|
|
111
|
+
GeeqTxUnknown,
|
|
112
|
+
GeeqTxUnknownReason,
|
|
113
|
+
} from './socket/tracked-tx'
|
|
114
|
+
export type { GeeqTxStatusEvent, GeeqTxVerdict } from './socket/tx-status'
|
|
115
|
+
export type {
|
|
116
|
+
GeeqWebSocketFactory,
|
|
117
|
+
GeeqWebSocketLike,
|
|
118
|
+
GeeqWebSocketMessageEvent,
|
|
119
|
+
} from './net/websocket'
|
|
120
|
+
|
|
121
|
+
// Content, encoding and metadata — everything an application would otherwise
|
|
122
|
+
// hand-roll around a transaction
|
|
123
|
+
export {
|
|
124
|
+
ATTEST_HASH_BYTES,
|
|
125
|
+
assertAttestHash,
|
|
126
|
+
attestHashOf,
|
|
127
|
+
attestHashOfSync,
|
|
128
|
+
readContent,
|
|
129
|
+
} from './tx/content'
|
|
130
|
+
export type { GeeqContent } from './tx/content'
|
|
131
|
+
export { inspectMetadata } from './tx/metadata'
|
|
132
|
+
export type { MetadataEntryReport, MetadataReport } from './tx/metadata'
|
|
133
|
+
export { fromBase64, toBase64 } from './encoding/base64'
|
|
134
|
+
export { fromHex, toHex } from './encoding/hex'
|
|
135
|
+
|
|
136
|
+
// Ledgers
|
|
137
|
+
export { decodeValidationLedger } from './ledger/decode'
|
|
138
|
+
export { GeeqLedger } from './ledger/geeq-ledger'
|
|
139
|
+
export type { DecodeLedgerOptions } from './ledger/decode'
|
|
140
|
+
export type { GeeqCoinAccount, GeeqCoinRecordKind, GeeqHeldNft } from './ledger/coin-accounts'
|
|
141
|
+
export type { GeeqLedgerNode, GeeqNodeRole, GeeqNodeStatus } from './ledger/net-actors'
|
|
142
|
+
export type { GeeqProxyAccount, GeeqProxyKind } from './ledger/proxy-accounts'
|
|
143
|
+
|
|
144
|
+
// Transactions
|
|
145
|
+
export { createAttestationTx, createAttestationTxSync } from './tx/attestation'
|
|
146
|
+
export { createCoinTransferTx, createCoinTransferTxSync } from './tx/transfer'
|
|
147
|
+
export { createNftMintProxyTx, createNftMintProxyTxSync } from './tx/proxy'
|
|
148
|
+
export { createNftMintTx, createNftMintTxSync } from './tx/nft'
|
|
149
|
+
|
|
150
|
+
// Amounts — the protocol counts pennies; how many make a Geeq is the app's to say
|
|
151
|
+
export { formatGeeq, toPennies } from './amount'
|
|
152
|
+
export type { GeeqAmount } from './amount'
|
|
153
|
+
|
|
154
|
+
// Errors
|
|
155
|
+
export { GeeqError } from './errors'
|
|
156
|
+
export type { GeeqErrorCode } from './errors'
|
|
157
|
+
|
|
158
|
+
// Types
|
|
159
|
+
export type {
|
|
160
|
+
AttestationContent,
|
|
161
|
+
AttestationTx,
|
|
162
|
+
AttestationTxFlatInput,
|
|
163
|
+
AttestationTxInput,
|
|
164
|
+
AttestationTxStateInput,
|
|
165
|
+
ChainStateInput,
|
|
166
|
+
CoinTransferContent,
|
|
167
|
+
CoinTransferTx,
|
|
168
|
+
CoinTransferTxFlatInput,
|
|
169
|
+
CoinTransferTxInput,
|
|
170
|
+
CoinTransferTxStateInput,
|
|
171
|
+
GeeqChainState,
|
|
172
|
+
GeeqFee,
|
|
173
|
+
GeeqMetadataLimits,
|
|
174
|
+
DecodedWalletAddress,
|
|
175
|
+
GeeqCoinType,
|
|
176
|
+
GeeqKeypair,
|
|
177
|
+
GeeqRecordType,
|
|
178
|
+
InitOptions,
|
|
179
|
+
MnemonicOptions,
|
|
180
|
+
GeeqNft,
|
|
181
|
+
GeeqNftMintProxies,
|
|
182
|
+
GeeqNftMintProxy,
|
|
183
|
+
NftMintProxyTx,
|
|
184
|
+
NftMintProxyTxInput,
|
|
185
|
+
NftMintTx,
|
|
186
|
+
NftMintTxInput,
|
|
187
|
+
NftTransferEntry,
|
|
188
|
+
WalletAddressOptions,
|
|
189
|
+
WasmSource,
|
|
190
|
+
} from './types'
|
|
191
|
+
|
|
192
|
+
export type {
|
|
193
|
+
AccountIdentifier,
|
|
194
|
+
CreateWalletAddressInput,
|
|
195
|
+
} from './address/wallet-address'
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { CoinAcctRec } from '../capnp/coinAcctRec.capnp'
|
|
2
|
+
|
|
3
|
+
import { uint64ToPennies } from '../amount'
|
|
4
|
+
|
|
5
|
+
/** Which coin record subtype an account is. Only `'asset'` may sign an attestation. */
|
|
6
|
+
export type GeeqCoinRecordKind = 'asset' | 'hl' | 'micro' | 'unknown'
|
|
7
|
+
|
|
8
|
+
/** One coin account record, as plain values. */
|
|
9
|
+
export interface GeeqCoinAccount {
|
|
10
|
+
readonly accountId: Uint8Array
|
|
11
|
+
readonly kind: GeeqCoinRecordKind
|
|
12
|
+
/**
|
|
13
|
+
* `undefined` for a hash-lock record, which carries no nonce field at all, and for a
|
|
14
|
+
* record whose subtype could not be read.
|
|
15
|
+
*/
|
|
16
|
+
readonly nonce: number | undefined
|
|
17
|
+
/** Balance in pennies. Use `formatGeeq` to show it. */
|
|
18
|
+
readonly balance: bigint
|
|
19
|
+
/** The NFTs it holds, in the order its record lists them. Read for coin-asset records only. */
|
|
20
|
+
readonly nfts: readonly GeeqHeldNft[]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** One NFT an account holds, as its record lists it. */
|
|
24
|
+
export interface GeeqHeldNft {
|
|
25
|
+
/** The NFT's 32-byte id. */
|
|
26
|
+
readonly id: Uint8Array
|
|
27
|
+
/** A counterparty NFT moves only with a counterparty transaction, never a plain transfer. */
|
|
28
|
+
readonly counterparty: boolean
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** What we keep per account. The id is the map key, so it is not repeated here. */
|
|
32
|
+
export interface CoinAccountFacts {
|
|
33
|
+
readonly kind: GeeqCoinRecordKind
|
|
34
|
+
readonly nonce: number | undefined
|
|
35
|
+
readonly balance: bigint
|
|
36
|
+
readonly nfts: readonly GeeqHeldNft[]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Read a coin account's subtype and nonce without tripping the union's zero value.
|
|
41
|
+
*
|
|
42
|
+
* `getAsset()` throws on a record of another subtype, and — worse — `ASSET` is the
|
|
43
|
+
* union's zero value, so a record whose tag is unset and whose pointer is null passes
|
|
44
|
+
* capnp-ts's `testWhich`. `getStruct` then calls `initStruct`, MUTATING the decoded
|
|
45
|
+
* message and handing back a zeroed struct whose `getNonce()` is `0`. A silent nonce of
|
|
46
|
+
* zero is the worst possible failure here, so every arm is guarded by both `isX()` and
|
|
47
|
+
* `hasX()` and an unreadable record reports `'unknown'` instead of a number.
|
|
48
|
+
*/
|
|
49
|
+
export function readCoinAccount(record: CoinAcctRec): CoinAccountFacts {
|
|
50
|
+
const recType = record.getRecType()
|
|
51
|
+
// The balance lives on the outer record, so every subtype has one.
|
|
52
|
+
const balance = uint64ToPennies(record.getAmtCoin())
|
|
53
|
+
|
|
54
|
+
if (recType.isAsset()) {
|
|
55
|
+
if (!recType.hasAsset()) return { kind: 'unknown', nonce: undefined, balance, nfts: [] }
|
|
56
|
+
const asset = recType.getAsset()
|
|
57
|
+
const nfts = asset.getNftAssetSubgroups().toArray().map((nft) => ({
|
|
58
|
+
id: new Uint8Array(nft.getAcctNumApp().toArrayBuffer()),
|
|
59
|
+
counterparty: nft.getFlagCounterpartyNft(),
|
|
60
|
+
}))
|
|
61
|
+
return { kind: 'asset', nonce: asset.getNonce(), balance, nfts }
|
|
62
|
+
}
|
|
63
|
+
if (recType.isHl()) {
|
|
64
|
+
// Hash-lock records have no nonce field in the schema at all.
|
|
65
|
+
return { kind: 'hl', nonce: undefined, balance, nfts: [] }
|
|
66
|
+
}
|
|
67
|
+
if (recType.isMicro()) {
|
|
68
|
+
return recType.hasMicro()
|
|
69
|
+
? { kind: 'micro', nonce: recType.getMicro().getNonce(), balance, nfts: [] }
|
|
70
|
+
: { kind: 'unknown', nonce: undefined, balance, nfts: [] }
|
|
71
|
+
}
|
|
72
|
+
return { kind: 'unknown', nonce: undefined, balance, nfts: [] }
|
|
73
|
+
}
|