@qorechain/sdk 0.7.0 → 0.8.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/README.md CHANGED
@@ -108,8 +108,8 @@ console.log(result.transactionHash);
108
108
  QoreChain supports post-quantum cryptography via ML-DSA-87 (Dilithium-5) and a
109
109
  hybrid posture. The key/sign/verify primitives are available today through
110
110
  `generatePqcKeypair`, `pqcSign`, `pqcVerify`, and the pluggable `PqcSigner` /
111
- `HybridSigner`. Hybrid transaction submission is being finalized for the live
112
- network.
111
+ `HybridSigner`, and hybrid transactions end-to-end through `buildHybridTx` /
112
+ `signAndBroadcastHybrid`.
113
113
 
114
114
  ```ts
115
115
  import { generatePqcKeypair, pqcSign, pqcVerify } from "@qorechain/sdk";
@@ -120,6 +120,53 @@ const signature = pqcSign(keypair.secretKey, message);
120
120
  const ok = pqcVerify(keypair.publicKey, message, signature);
121
121
  ```
122
122
 
123
+ #### Hybrid sign-bytes: v1 / v2 per network (v0.8.0)
124
+
125
+ The ML-DSA-87 half of a hybrid transaction signs the body *without* the PQC
126
+ extension (`B0`) and the AuthInfo bytes (`A`), in one of two forms:
127
+
128
+ ```
129
+ v1: BE32(len B0) ‖ B0 ‖ BE32(len A) ‖ A
130
+ v2: "qorechain-pqc-hybrid-v2" ‖ BE64(len chainId) ‖ chainId ‖ BE32(len B0) ‖ B0 ‖ BE32(len A) ‖ A
131
+ ```
132
+
133
+ A network verifies exactly **one** form at any height. `qorechain-diana`
134
+ (testnet) switched to v2 at its `v3.1.98` upgrade; `qorechain-vladi` (mainnet)
135
+ stays on v1 until its own, named `v3.2.0`; chains born later are v2 from genesis. So pass
136
+ the network's REST endpoint and let the SDK ask (`signBytesVersion: "auto"`, the
137
+ default):
138
+
139
+ ```ts
140
+ import { signAndBroadcastHybrid, isHybridSignBytesRejection } from "@qorechain/sdk";
141
+
142
+ const res = await signAndBroadcastHybrid({
143
+ registry, signer, pqcKeypair, messages, fee,
144
+ chainId: "qorechain-diana",
145
+ rest: "https://api-testnet.qore.host", // picks v1 or v2 for this network
146
+ accountNumber, sequence,
147
+ transport, // e.g. a connected StargateClient
148
+ });
149
+ ```
150
+
151
+ - `"auto"` reads `GET {rest}/cosmos/upgrade/v1beta1/applied_plan/<name>` for each
152
+ name in `SIGN_BYTES_V2_UPGRADES` (`"v3.2.0"`, then `"v3.1.98"`), stopping at the
153
+ first applied height above 0, and caches the answer for about a minute. Both
154
+ names matter: the testnet switched under `v3.1.98` and keeps that record,
155
+ mainnet switches under `v3.2.0`. A client that asks for only one answers v1 on
156
+ the other network, and the chain then refuses every hybrid tx with `pqc` code 21.
157
+ - On `qorechain-vladi` / `qorechain-diana`, `"auto"` without `rest` — or with a
158
+ node that cannot be asked — **throws** rather than guess. Force a form with
159
+ `signBytesVersion: "v1"` or `"v2"`.
160
+ - `signAndBroadcastHybrid` and `EthNativeSigner.signAndBroadcast` re-resolve and
161
+ retry once on a `pqc` code 21 refusal. If you broadcast yourself, catch the
162
+ refusal with `isHybridSignBytesRejection(err)` and rebuild with
163
+ `buildHybridTx({ ..., forceRefreshSignBytesVersion: true })`.
164
+ - `built.signBytesVersion` reports the form a built transaction used.
165
+ - The same rule covers the PQC key-migration payload (`migrationSignBytes`) and
166
+ the bridge attestation payload (`bridgeAttestationSignBytes`).
167
+ - `rest` is optional in the TypeScript types, so a caller that forgets it compiles cleanly and only fails at runtime on `qorechain-vladi` / `qorechain-diana`. Cover your wiring with a runtime test, not just a type check.
168
+ - In unit tests, pass `signBytesVersion: "v1"` or `"v2"` explicitly (or inject `fetch`): `"auto"` asks the network, so a test that omits it silently depends on a live node.
169
+
123
170
  ### CosmWasm contracts
124
171
 
125
172
  Interact with CosmWasm contracts via thin wrappers over
@@ -334,11 +381,7 @@ post-quantum signature. The account parser (`parseEthPubkeyAny`) reads
334
381
  account number / sequence from an eth_secp256k1 on-chain pubkey.
335
382
 
336
383
  ```ts
337
- import {
338
- deriveUnifiedAccount,
339
- unifiedAccountFromPhantomSignature,
340
- signHybridEth,
341
- } from "@qorechain/sdk";
384
+ import { deriveUnifiedAccount, resolveSignBytesVersion, signHybridEth } from "@qorechain/sdk";
342
385
 
343
386
  const account = await deriveUnifiedAccount(mnemonic);
344
387
  account.cosmos; // "qor1…" — QoreChain Native lane
@@ -346,22 +389,34 @@ account.evm; // "0x…" — EIP-55 checksummed
346
389
  account.svm; // "<base58>" — 20 bytes + 12 zero pad
347
390
 
348
391
  // Sign a QoreChain Native tx from the unified eth key (hybrid PQC path).
392
+ // signHybridEth is synchronous: resolve the sign-bytes form for the network
393
+ // first (v1 on mainnet until its v3.1.98 upgrade, v2 after).
394
+ const signBytesVersion = await resolveSignBytesVersion({
395
+ chainId: "qorechain-vladi",
396
+ rest: "https://api.qore.host",
397
+ });
349
398
  const signed = signHybridEth({
350
- signingKey: { privateKey: account.privateKey, publicKey: account.publicKey },
351
- pqc: account.pqc,
399
+ account, // privateKey, publicKey and the pqc keypair
352
400
  messages: [msg.cosmos.send(/* … */)],
353
401
  chainId: "qorechain-vladi",
354
402
  accountNumber,
355
403
  sequence,
356
404
  fee,
405
+ signBytesVersion,
406
+ encodeMessage: (m) => registry.encodeAsAny(m),
357
407
  });
358
408
 
359
- // Phantom P1a: derive a canonical, non-custodial unified account from a
360
- // deterministic Phantom signature (shake256(signature, 32)).
361
- const fromPhantom = unifiedAccountFromPhantomSignature(phantomSignature);
362
- // or connectPhantomUnified(provider) to run the connect → sign → derive flow.
409
+ // Or let EthNativeSigner resolve and retry for you:
410
+ // new EthNativeSigner(account, { rest: "https://api.qore.host" }).signAndBroadcast(transport, params)
363
411
  ```
364
412
 
413
+ `unifiedAccountFromSeed` uses the 32 bytes it is given **as** the spend key, so
414
+ pass real secret entropy — a CSPRNG seed or a mnemonic-derived key — never a
415
+ wallet signature or any other value a third party can request. (The signature-
416
+ derived helpers `unifiedAccountFromPhantomSignature` / `connectPhantomUnified`
417
+ were removed in v0.8.0 for exactly that reason and now throw.) To let an
418
+ external wallet key act for an account, use the authenticator lanes below.
419
+
365
420
  See the [unified-wallet](../../docs/docs/guides/unified-wallet.md) guide.
366
421
 
367
422
  ### Cross-VM message reads