@monolythium/core-sdk 0.1.0 → 0.2.0

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @monolythium/core-sdk
2
2
 
3
- Official TypeScript SDK for [Monolythium v4.0](https://monolythium.com) (LythiumDAG-BFT).
3
+ Official TypeScript SDK for [Monolythium v4.1](https://monolythium.com) (LythiumDAG-BFT).
4
4
 
5
5
  ## Install
6
6
 
@@ -29,34 +29,49 @@ const markets = await client.lythClobMarkets(25);
29
29
  console.log(decoded.status, holders.holders.length, txs.transactions.length, markets.markets.length);
30
30
  ```
31
31
 
32
- The client wraps every JSON-RPC method served by a Monolythium node — the
33
- EVM-compatible `eth_*` / `net_*` / `web3_*` surface and the chain-native
34
- `lyth_*` and `debug_*` namespaces, including live explorer helpers such as
32
+ The client wraps the Monolythium node JSON-RPC surface: current chain-native
33
+ `lyth_*` methods, passive compatibility `eth_*` / `net_*` / `web3_*` reads, and
34
+ server-gated debug methods. The no-EVM v4.1 path should use native helpers such
35
+ as
35
36
  `lyth_decodeTx`, `lyth_gapRecords`, `lyth_dagParents`, `lyth_richList`,
36
37
  `lyth_txFeed`, `lyth_addressProfile`, `lyth_addressFlow`, `lyth_search`,
37
38
  `lyth_chainStats`, `lyth_clobMarkets`, `lyth_clobTrades`, `lyth_clobOhlc`,
38
- `lyth_clobOrderBook`, and `lyth_addressActivityKind`. Wire types are generated
39
- from the Rust SDK via `ts-rs` where possible, with SDK-local convenience types
40
- for newer explorer envelopes.
39
+ `lyth_clobOrderBook`, `lyth_nativeReceipt`, and `lyth_addressActivityKind`.
40
+ Wire types are generated from the Rust SDK via `ts-rs` where possible, with
41
+ SDK-local convenience types for newer explorer envelopes.
41
42
 
42
43
  Quantities surface as `bigint` to preserve full precision. Use
43
44
  `parseQuantity` only when you know the value fits in `Number.MAX_SAFE_INTEGER`.
44
45
 
46
+ ### MRC accounts
47
+
48
+ ```ts
49
+ const mrcAccount = await client.lythMrcAccount("monos1effvdw0d05a35j69wwxplhmctpcclx382n60yf", 10);
50
+ console.log(mrcAccount.smartAccount?.controller, mrcAccount.policySpends.length);
51
+ ```
52
+
45
53
  ### Node API client
46
54
 
47
55
  The SDK also exports `ApiClient` for the node's REST-shaped `/api/v1` surface.
48
56
  Pass the JSON-RPC endpoint; the client derives `/api/v1` automatically.
49
57
 
50
58
  ```ts
51
- import { ApiClient } from "@monolythium/core-sdk";
59
+ import { ApiClient, addressToTypedBech32 } from "@monolythium/core-sdk";
52
60
 
53
61
  const api = new ApiClient("https://rpc.testnet.monolythium.com");
54
62
 
55
63
  const latest = await api.block("latest");
56
64
  const txs = await api.blockTransactions("latest", 0, 25);
57
- const activity = await api.addressActivity("0x123456789abcdef0112233445566778899aabbcc");
58
-
59
- console.log(latest.data.block.blockHash, txs.data.totalTransactions, activity.data.entries.length);
65
+ const nativeReceipt = await api.transactionNativeReceipt("0x...");
66
+ const account = addressToTypedBech32("user", "0x123456789abcdef0112233445566778899aabbcc");
67
+ const activity = await api.addressActivity(account);
68
+
69
+ console.log(
70
+ latest.data.block.blockHash,
71
+ txs.data.totalTransactions,
72
+ nativeReceipt.data.eventCount,
73
+ activity.data.entries.length,
74
+ );
60
75
  ```
61
76
 
62
77
  ### Chain registry
@@ -86,14 +101,72 @@ the first one that answers with the expected chain id.
86
101
 
87
102
  ### Address helpers
88
103
 
89
- User-facing surfaces should display Monolythium addresses as `mono1...`
90
- bech32m, while JSON-RPC still uses 20-byte `0x...` hex.
104
+ Public v4.1 SDK, JSON-RPC, REST, wallet, and explorer surfaces use ADR-0038
105
+ typed bech32m addresses. User accounts use `mono1...`; smart-account MRC
106
+ lookups use `monos1...`; RISC-V contracts use `monoc1...`. Raw `0x` strings
107
+ remain only for low-level byte conversion helpers and compatibility adapters.
91
108
 
92
109
  ```ts
93
110
  import { addressToBech32, bech32ToAddress } from "@monolythium/core-sdk";
94
111
 
95
112
  const display = addressToBech32("0x123456789abcdef0112233445566778899aabbcc");
96
- const wire = bech32ToAddress(display);
113
+ const hex = bech32ToAddress(display);
114
+ ```
115
+
116
+ V4.1 typed address helpers also encode role-specific HRPs such as `monoc` for
117
+ RISC-V contracts:
118
+
119
+ ```ts
120
+ import { addressToTypedBech32, typedBech32ToAddress } from "@monolythium/core-sdk";
121
+
122
+ const contract = addressToTypedBech32("contract", "0x2222222222222222222222222222222222222222");
123
+ const decoded = typedBech32ToAddress(contract, "contract");
124
+ ```
125
+
126
+ ### MRV / RISC-V helpers
127
+
128
+ The package includes the first v4.1 MRV SDK slice: artifact metadata
129
+ validation, MRV v1 transaction extension descriptors, typed contract address
130
+ helpers, and native deploy/call request builders with lythoshi and
131
+ execution-unit fields.
132
+
133
+ ```ts
134
+ import {
135
+ MRV_FORMAT_VERSION,
136
+ MRV_PROFILE_MONO_RV32IM_V1,
137
+ buildMrvCallPlan,
138
+ buildMrvDeployPlan,
139
+ deriveMrvContractAddress,
140
+ mrvAddressToBech32,
141
+ mrvCodeHashHex,
142
+ validateMrvArtifactMetadata,
143
+ } from "@monolythium/core-sdk";
144
+
145
+ const code = new Uint8Array([0x13, 0x00, 0x00, 0x00]);
146
+ const metadata = {
147
+ formatVersion: MRV_FORMAT_VERSION,
148
+ profile: MRV_PROFILE_MONO_RV32IM_V1,
149
+ codeHash: mrvCodeHashHex(code),
150
+ codeBytes: 4n,
151
+ debugBytes: 0n,
152
+ abi: { symbols: [{ name: "transfer", kind: "function", inputs: [], outputs: [] }] },
153
+ imports: [{ module: "mono", name: "emit_event", id: 0x0302 }],
154
+ memory: { initialPages: 1, maxPages: 4, stackBytes: 16384 },
155
+ storageNamespace: { name: "contract_state", version: 1 },
156
+ build: { toolchain: "mono-riscv", sourceDigest: `0x${"00".repeat(32)}`, profile: "release" },
157
+ };
158
+
159
+ const validated = validateMrvArtifactMetadata(metadata, code);
160
+ const deployer = mrvAddressToBech32("user", new Uint8Array(20).fill(0x11));
161
+ const deployAddress = deriveMrvContractAddress(deployer, 7n, validated.codeHash);
162
+ const deploy = buildMrvDeployPlan("0x13000000", {
163
+ from: deployer,
164
+ nonce: 7n,
165
+ artifactHash: validated.codeHash,
166
+ executionUnitLimit: 1_000_000n,
167
+ });
168
+ const call = buildMrvCallPlan(deployAddress, [0x01, 0x02]);
169
+ console.log(validated.syscalls, deploy.extension, deploy.expectedContractAddress, call.request);
97
170
  ```
98
171
 
99
172
  ### Spending-policy helpers
@@ -102,6 +175,8 @@ Fresh sub-account policy claims must use `claimPolicyByAddress` or
102
175
  `setPolicyClaim`, both of which bind the policy to a sub-account ML-DSA-65
103
176
  signature. Prefer `claimPolicyByAddress` after the pubkey is registered in
104
177
  pubkey-registry; it avoids carrying the 1952-byte pubkey in calldata.
178
+ `policyArgs.subAccount` and `policyArgs.principal` must be typed `mono1...`
179
+ bech32m account addresses.
105
180
 
106
181
  ```ts
107
182
  import {
@@ -122,16 +197,31 @@ primary ML-DSA-65 pubkey once, so contracts can look it up by address.
122
197
 
123
198
  ```ts
124
199
  import {
200
+ addressToTypedBech32,
125
201
  encodeRegisterPubkeyCalldata,
126
202
  encodeLookupPubkeyCalldata,
127
203
  decodeLookupPubkeyReturn,
128
204
  } from "@monolythium/core-sdk";
129
205
 
206
+ const account = addressToTypedBech32("user", "0x123456789abcdef0112233445566778899aabbcc");
130
207
  const calldata = encodeRegisterPubkeyCalldata(mlDsa65Pubkey);
131
- const lookup = encodeLookupPubkeyCalldata("0x123456789abcdef0112233445566778899aabbcc");
208
+ const lookup = encodeLookupPubkeyCalldata(account);
132
209
  const decoded = decodeLookupPubkeyReturn(returnData);
133
210
  ```
134
211
 
212
+ ### Bridge route readiness
213
+
214
+ Bridge helpers assess explicit route disclosures and fail closed. The SDK can
215
+ select a route for an intent, but it cannot produce a live quote or submit
216
+ payload until `mono-core` exposes those API/runtime primitives.
217
+
218
+ ```ts
219
+ import { bridgeQuoteSubmitReadiness } from "@monolythium/core-sdk";
220
+
221
+ const readiness = bridgeQuoteSubmitReadiness(intent, routeDisclosures);
222
+ console.log(readiness.routeSelectionReady, readiness.quoteReady, readiness.blockedReasons);
223
+ ```
224
+
135
225
  ### PQM-1 + ML-DSA-65 helpers
136
226
 
137
227
  Wallets and faucets can derive deterministic ML-DSA-65 backends directly in
@@ -150,26 +240,27 @@ const backend = pqm1MnemonicToMlDsa65Backend(mnemonic);
150
240
  const signature = backend.sign(new Uint8Array([1, 2, 3]));
151
241
  ```
152
242
 
153
- ### ethers.js v6 compat
243
+ ### Legacy ethers.js v6 compat
154
244
 
155
- The package also ships an ethers v6 compat shim a `MonolythiumProvider`
156
- and `MonolythiumSigner` that drop in wherever ethers' `Provider` and
157
- `Signer` are expected. `ethers` is a peer dependency.
245
+ The package still ships an ethers v6 compat shim for legacy migration tooling:
246
+ a `MonolythiumProvider` and `MonolythiumSigner` that drop in wherever ethers'
247
+ `Provider` and `Signer` are expected. It is not the v4.1 no-EVM deployment path;
248
+ new app work should use the MRV/RISC-V builders and `lyth_*` read surfaces.
249
+ `ethers` is a peer dependency for this compatibility path.
158
250
 
159
251
  ```ts
160
- import { Wallet, ContractFactory } from "ethers";
252
+ import { Wallet } from "ethers";
161
253
  import {
162
254
  MonolythiumProvider,
163
255
  MonolythiumSigner,
164
- } from "@monolythium/core-sdk";
256
+ } from "@monolythium/core-sdk/ethers";
165
257
 
166
258
  const provider = new MonolythiumProvider("https://rpc.testnet.monolythium.com");
167
259
  const wallet = new Wallet(process.env.PRIVATE_KEY!);
168
260
  const signer = MonolythiumSigner.fromEthersWallet(wallet, provider);
169
261
 
170
- const factory = new ContractFactory(abi, bytecode, signer);
171
- const contract = await factory.deploy();
172
- await contract.waitForDeployment();
262
+ // Legacy-only adapter setup. Do not use this path for new v4.1 MRV deployments.
263
+ console.log(await signer.getAddress());
173
264
  ```
174
265
 
175
266
  For non-secp256k1 signing sources (OS keychain, hardware wallet, future
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var mlDsa_js = require('@noble/post-quantum/ml-dsa.js');
4
+ var blake3_js = require('@noble/hashes/blake3.js');
4
5
  var sha3_js = require('@noble/hashes/sha3.js');
5
6
  var bip39 = require('@scure/bip39');
6
7
  var english_js = require('@scure/bip39/wordlists/english.js');
@@ -145,8 +146,7 @@ function encodeTransactionForHash(fields, tag) {
145
146
  n.input,
146
147
  new Uint8Array(4),
147
148
  // access_list length
148
- new Uint8Array(4)
149
- // extensions length
149
+ encodeExtensionsForHash(n.extensions)
150
150
  );
151
151
  }
152
152
  function bincodeSignedTransaction(fields, signature, publicKey) {
@@ -163,7 +163,8 @@ function bincodeSignedTransaction(fields, signature, publicKey) {
163
163
  w.rawBytes(uint256Le(n.value, "value"));
164
164
  w.bytes(n.input);
165
165
  w.u64(0n);
166
- w.u64(0n);
166
+ w.u64(BigInt(n.extensions.length));
167
+ for (const ext of n.extensions) bincodeTypedExtensionInto(w, ext);
167
168
  bincodeMlDsa65OpaqueInto(w, sig);
168
169
  bincodeMlDsa65OpaqueInto(w, pk);
169
170
  return w.toBytes();
@@ -177,7 +178,8 @@ function normalizeTxFields(fields) {
177
178
  gasLimit: parseBigint(fields.gasLimit, "gasLimit"),
178
179
  to: normalizeTo(fields.to),
179
180
  value: parseBigint(fields.value, "value"),
180
- input: normalizeBytes(fields.input ?? new Uint8Array(0), "input")
181
+ input: normalizeBytes(fields.input ?? new Uint8Array(0), "input"),
182
+ extensions: normalizeExtensions(fields.extensions)
181
183
  };
182
184
  }
183
185
  function normalizeTo(value) {
@@ -189,6 +191,30 @@ function normalizeBytes(value, label) {
189
191
  if (typeof value === "string") return hexToBytes(value, label);
190
192
  return value instanceof Uint8Array ? value : Uint8Array.from(value);
191
193
  }
194
+ function normalizeExtensions(value) {
195
+ if (value === void 0) return [];
196
+ return value.map((ext, index) => {
197
+ if (!Number.isInteger(ext.kind) || ext.kind < 0 || ext.kind > 255) {
198
+ throw new Error(`extensions[${index}].kind out of u8 range`);
199
+ }
200
+ const body = normalizeBytes("bodyHex" in ext ? ext.bodyHex : ext.body, `extensions[${index}].body`);
201
+ if (body.length > 4294967295) {
202
+ throw new Error(`extensions[${index}].body exceeds u32 length`);
203
+ }
204
+ return { kind: ext.kind, body };
205
+ });
206
+ }
207
+ function encodeExtensionsForHash(extensions) {
208
+ const chunks = [bigintToBeBytes(BigInt(extensions.length), 4, "extensions.length")];
209
+ for (const ext of extensions) {
210
+ chunks.push(
211
+ Uint8Array.of(ext.kind),
212
+ bigintToBeBytes(BigInt(ext.body.length), 4, "extension.body.length"),
213
+ ext.body
214
+ );
215
+ }
216
+ return concatBytes(...chunks);
217
+ }
192
218
  function uint256Le(value, label) {
193
219
  if (value < 0n || value >= 1n << 256n) throw new Error(`${label} out of u256 range`);
194
220
  const out = new Uint8Array(32);
@@ -204,6 +230,10 @@ function bincodeMlDsa65OpaqueInto(w, raw) {
204
230
  w.u16(STANDARD_ALGO_NUMBER_ML_DSA_65);
205
231
  w.bytes(raw);
206
232
  }
233
+ function bincodeTypedExtensionInto(w, ext) {
234
+ w.u8(ext.kind);
235
+ w.bytes(ext.body);
236
+ }
207
237
 
208
238
  // src/crypto/ml-dsa.ts
209
239
  var ML_DSA_65_SEED_LEN = 32;
@@ -212,6 +242,8 @@ var ML_DSA_65_PUBLIC_KEY_LEN = 1952;
212
242
  var ML_DSA_65_SIGNATURE_LEN = 3309;
213
243
  var STANDARD_ALGO_NUMBER_ML_DSA_65 = 1001;
214
244
  var ENUM_VARIANT_INDEX_ML_DSA_65 = 5;
245
+ var ADDRESS_DERIVATION_DOMAIN = "MONO_ADDRESS_BLAKE3_20_V1";
246
+ var ADDRESS_DERIVATION_DOMAIN_BYTES = new TextEncoder().encode(ADDRESS_DERIVATION_DOMAIN);
215
247
  var MlDsa65Backend = class _MlDsa65Backend {
216
248
  #secretKey;
217
249
  #publicKey;
@@ -219,7 +251,7 @@ var MlDsa65Backend = class _MlDsa65Backend {
219
251
  constructor(secretKey, publicKey) {
220
252
  this.#secretKey = expectBytes(secretKey, ML_DSA_65_SIGNING_KEY_LEN, "ML-DSA-65 secret key").slice();
221
253
  this.#publicKey = expectBytes(publicKey, ML_DSA_65_PUBLIC_KEY_LEN, "ML-DSA-65 public key").slice();
222
- this.#addressBytes = sha3_js.keccak_256(this.#publicKey).slice(12);
254
+ this.#addressBytes = mlDsa65AddressBytes(this.#publicKey);
223
255
  }
224
256
  static fromSeed(seed) {
225
257
  const kp = mlDsa_js.ml_dsa65.keygen(expectBytes(seed, ML_DSA_65_SEED_LEN, "ML-DSA-65 seed"));
@@ -268,7 +300,15 @@ var MlDsa65Backend = class _MlDsa65Backend {
268
300
  }
269
301
  };
270
302
  function mlDsa65AddressFromPublicKey(publicKey) {
271
- return bytesToHex(sha3_js.keccak_256(expectBytes(publicKey, ML_DSA_65_PUBLIC_KEY_LEN, "ML-DSA-65 public key")).slice(12));
303
+ return bytesToHex(mlDsa65AddressBytes(publicKey));
304
+ }
305
+ function mlDsa65AddressBytes(publicKey) {
306
+ const bytes = expectBytes(publicKey, ML_DSA_65_PUBLIC_KEY_LEN, "ML-DSA-65 public key");
307
+ return blake3_js.blake3(concatBytes(
308
+ ADDRESS_DERIVATION_DOMAIN_BYTES,
309
+ bigintToBeBytes(BigInt(STANDARD_ALGO_NUMBER_ML_DSA_65), 2, "ML-DSA-65 algo id"),
310
+ bytes
311
+ )).slice(0, 20);
272
312
  }
273
313
  function encodeMlDsa65Opaque(raw) {
274
314
  const bytes = raw instanceof Uint8Array ? raw : Uint8Array.from(raw);
@@ -387,6 +427,8 @@ var MempoolClass = {
387
427
  PrivacyOp: 2,
388
428
  CLOBOp: 3,
389
429
  AgentOp: 4,
430
+ FoundationOp: 5,
431
+ /** @deprecated Use FoundationOp. */
390
432
  GovernanceOp: 5,
391
433
  RWAOp: 6
392
434
  };
@@ -490,7 +532,6 @@ async function fetchEncryptionKey(client) {
490
532
  };
491
533
  }
492
534
  async function buildEncryptedSubmission(args) {
493
- const signed = args.backend.signEvmTx(args.tx);
494
535
  const input = normalizeInput(args.tx.input);
495
536
  const to = normalizeTo2(args.tx.to);
496
537
  const nonceAad = {
@@ -498,10 +539,14 @@ async function buildEncryptedSubmission(args) {
498
539
  nonce: parseBigint(args.tx.nonce, "nonce"),
499
540
  chainId: parseBigint(args.tx.chainId, "chainId"),
500
541
  class: args.class ?? (to !== null && input.length === 0 ? MempoolClass.Transfer : MempoolClass.ContractCall),
501
- maxFeePerGas: u128Saturate(parseBigint(args.tx.maxFeePerGas, "maxFeePerGas")),
502
- maxPriorityFeePerGas: u128Saturate(parseBigint(args.tx.maxPriorityFeePerGas, "maxPriorityFeePerGas")),
542
+ maxFeePerGas: u128Checked(parseBigint(args.tx.maxFeePerGas, "maxFeePerGas"), "maxFeePerGas"),
543
+ maxPriorityFeePerGas: u128Checked(
544
+ parseBigint(args.tx.maxPriorityFeePerGas, "maxPriorityFeePerGas"),
545
+ "maxPriorityFeePerGas"
546
+ ),
503
547
  gasLimit: parseBigint(args.tx.gasLimit, "gasLimit")
504
548
  };
549
+ const signed = args.backend.signEvmTx(args.tx);
505
550
  const decryptionHint = { epoch: args.encryptionKey.epoch, scheme: 0 };
506
551
  const built = await buildEncryptedEnvelope({
507
552
  signedInnerTxBincode: signed.wireBytes,
@@ -515,16 +560,19 @@ async function buildEncryptedSubmission(args) {
515
560
  return {
516
561
  envelopeWireHex: built.wireHex,
517
562
  innerSighashHex: `0x${[...signed.sighash].map((b) => b.toString(16).padStart(2, "0")).join("")}`,
563
+ innerTxHashHex: bytesToHex(signed.txHash),
518
564
  innerWireBytes: signed.wireBytes.length
519
565
  };
520
566
  }
521
567
  async function submitEncryptedEnvelope(client, envelopeWireHex) {
522
568
  return client.call("lyth_submitEncrypted", [envelopeWireHex]);
523
569
  }
524
- function u128Saturate(value) {
570
+ function u128Checked(value, field) {
525
571
  const cap = (1n << 128n) - 1n;
526
- if (value < 0n) return 0n;
527
- return value > cap ? cap : value;
572
+ if (value < 0n || value > cap) {
573
+ throw new Error(`${field} must fit in u128 for encrypted nonce AAD`);
574
+ }
575
+ return value;
528
576
  }
529
577
  function normalizeTo2(value) {
530
578
  if (value === null) return null;
@@ -539,6 +587,7 @@ function normalizeInput(value) {
539
587
  return value instanceof Uint8Array ? value : Uint8Array.from(value);
540
588
  }
541
589
 
590
+ exports.ADDRESS_DERIVATION_DOMAIN = ADDRESS_DERIVATION_DOMAIN;
542
591
  exports.BincodeWriter = BincodeWriter;
543
592
  exports.DKG_AEAD_TAG_LEN = DKG_AEAD_TAG_LEN;
544
593
  exports.DKG_NONCE_LEN = DKG_NONCE_LEN;
@@ -580,6 +629,7 @@ exports.expectBytes = expectBytes;
580
629
  exports.fetchEncryptionKey = fetchEncryptionKey;
581
630
  exports.generatePqm1Mnemonic = generatePqm1Mnemonic;
582
631
  exports.hexToBytes = hexToBytes;
632
+ exports.mlDsa65AddressBytes = mlDsa65AddressBytes;
583
633
  exports.mlDsa65AddressFromPublicKey = mlDsa65AddressFromPublicKey;
584
634
  exports.outerSigDigest = outerSigDigest;
585
635
  exports.parsePqm1Payload = parsePqm1Payload;