@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.
- 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,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`.
|