@orbinum/sdk 0.17.0 → 0.19.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/dist/index.d.mts +188 -23
- package/dist/index.d.ts +188 -23
- package/dist/index.js +155 -20
- package/dist/index.mjs +150 -20
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -105,6 +105,18 @@ declare class SubstrateClient {
|
|
|
105
105
|
private constructor();
|
|
106
106
|
private _dynamicBuilder;
|
|
107
107
|
private _extDecoder;
|
|
108
|
+
private _inflightTxCount;
|
|
109
|
+
/**
|
|
110
|
+
* `true` while any submitted transaction is still waiting for finalization.
|
|
111
|
+
* Connection managers use this to defer destroying the client — killing the
|
|
112
|
+
* WS mid-submit rejects the pending tx with "Client destroyed" even though
|
|
113
|
+
* it may still land on-chain.
|
|
114
|
+
*
|
|
115
|
+
* Only covers promise-based submits (`submit`, `submitUnsignedAndWatch`,
|
|
116
|
+
* `signAndSubmit`); observable-based `submitAndWatch` callers are not tracked.
|
|
117
|
+
*/
|
|
118
|
+
get hasInflightTx(): boolean;
|
|
119
|
+
private trackTx;
|
|
108
120
|
/**
|
|
109
121
|
* Connects to the Orbinum node via WebSocket.
|
|
110
122
|
* Throws if the node does not respond within `timeoutMs`.
|
|
@@ -285,14 +297,25 @@ declare class EvmClient {
|
|
|
285
297
|
}): Promise<bigint>;
|
|
286
298
|
/** Returns a transaction receipt by hash, or `null` if the transaction has not been mined yet. */
|
|
287
299
|
getTransactionReceipt(txHash: string): Promise<Record<string, unknown> | null>;
|
|
300
|
+
/**
|
|
301
|
+
* Fetches a transaction by hash, or `null` when the node no longer knows it
|
|
302
|
+
* (never mined and evicted from the pool). Unlike `request`, a `null`
|
|
303
|
+
* result is a valid answer here, not an error.
|
|
304
|
+
*/
|
|
305
|
+
getTransactionByHash(txHash: string): Promise<Record<string, unknown> | null>;
|
|
288
306
|
/**
|
|
289
307
|
* Polls `eth_getTransactionReceipt` until the transaction is included in a block.
|
|
290
308
|
*
|
|
309
|
+
* After `timeoutMs`, the tx-pool is consulted: a tx no longer known to the
|
|
310
|
+
* node is reported as dropped (safe to retry), while a tx still in the pool
|
|
311
|
+
* gets an extended grace window (up to 4× `timeoutMs`) before a "still
|
|
312
|
+
* pending" error — it may confirm later, so callers must NOT blindly retry.
|
|
313
|
+
*
|
|
291
314
|
* @param txHash - The transaction hash to wait for.
|
|
292
315
|
* @param intervalMs - Polling interval in milliseconds (default: 500).
|
|
293
316
|
* @param timeoutMs - Maximum time to wait in milliseconds (default: 60_000).
|
|
294
317
|
* @returns The transaction receipt once mined.
|
|
295
|
-
* @throws If the transaction is
|
|
318
|
+
* @throws If the transaction dropped, is still pending after the grace window, or reverted (`status == 0x0`).
|
|
296
319
|
*/
|
|
297
320
|
waitForReceipt(txHash: string, intervalMs?: number, timeoutMs?: number): Promise<Record<string, unknown>>;
|
|
298
321
|
}
|
|
@@ -1776,6 +1799,7 @@ declare class OrbinumClientProvider {
|
|
|
1776
1799
|
private _reconnectTimer;
|
|
1777
1800
|
private _stableTimer;
|
|
1778
1801
|
private _reconnectAttempt;
|
|
1802
|
+
private _probeFailures;
|
|
1779
1803
|
private _listeners;
|
|
1780
1804
|
/** Creates a new provider with the given configuration. Does not connect automatically — call `connect()` to initiate. */
|
|
1781
1805
|
constructor(config: ClientProviderConfig);
|
|
@@ -1806,6 +1830,21 @@ declare class OrbinumClientProvider {
|
|
|
1806
1830
|
* On failure: destroys any orphaned client and transitions to `'disconnected'`.
|
|
1807
1831
|
*/
|
|
1808
1832
|
private attemptConnect;
|
|
1833
|
+
/**
|
|
1834
|
+
* Consecutive failed probes required before the client is torn down. A
|
|
1835
|
+
* single missed probe is routine (throttled background tab, node busy
|
|
1836
|
+
* verifying a ZK proof, transient network blip) — destroying the client on
|
|
1837
|
+
* it rejects every in-flight request with "Client destroyed" even though
|
|
1838
|
+
* the tx may still land on-chain.
|
|
1839
|
+
*/
|
|
1840
|
+
private static readonly PROBE_FAILURE_THRESHOLD;
|
|
1841
|
+
/**
|
|
1842
|
+
* With a tx awaiting finalization, tolerate more missed probes: an unsigned
|
|
1843
|
+
* private_transfer/unshield makes the node CPU-bound on proof verification,
|
|
1844
|
+
* which is exactly when probes time out — tearing down then kills the very
|
|
1845
|
+
* tx being processed.
|
|
1846
|
+
*/
|
|
1847
|
+
private static readonly PROBE_FAILURE_THRESHOLD_INFLIGHT;
|
|
1809
1848
|
/** Starts the periodic heartbeat loop. Replaces any existing timer. */
|
|
1810
1849
|
private startHeartbeat;
|
|
1811
1850
|
/** Clears the heartbeat interval timer if active. */
|
|
@@ -2318,36 +2357,152 @@ declare const BABYJUB_SUBORDER = 27360303589799094027808007181571593860768139721
|
|
|
2318
2357
|
*/
|
|
2319
2358
|
declare function randomBlinding(): bigint;
|
|
2320
2359
|
|
|
2360
|
+
/**
|
|
2361
|
+
* SpendingKeyRequest
|
|
2362
|
+
*
|
|
2363
|
+
* What the user signs to derive their Orbinum spending key, per platform.
|
|
2364
|
+
*
|
|
2365
|
+
* These builders produce the *request* (typed data or message string). Turning a
|
|
2366
|
+
* signature into key material lives in `PrivacyKeys` — the split keeps the
|
|
2367
|
+
* platform-specific presentation away from the platform-agnostic KDF.
|
|
2368
|
+
*
|
|
2369
|
+
* SECURITY MODEL
|
|
2370
|
+
*
|
|
2371
|
+
* The signature seeds the spending key, so a deterministic signature over a
|
|
2372
|
+
* fixed, public string is a bearer token: any dapp the user connects to can
|
|
2373
|
+
* request the same string, obtain a byte-identical signature (ECDSA personal_sign
|
|
2374
|
+
* is deterministic, RFC-6979) and reconstruct the spending key, viewing key and
|
|
2375
|
+
* vault key. That is the v1 flaw these builders exist to close.
|
|
2376
|
+
*
|
|
2377
|
+
* The defense has two independent layers:
|
|
2378
|
+
*
|
|
2379
|
+
* 1. Message layer (here) — bind the signature to a domain the user can see.
|
|
2380
|
+
* EVM uses EIP-712, whose domain the wallet renders. Substrate has no
|
|
2381
|
+
* EIP-712, so the warning travels inside the signed text instead. NOTE this
|
|
2382
|
+
* layer is about VISIBILITY, not impossibility: nothing stops a hostile
|
|
2383
|
+
* origin from requesting the same payload, and no wallet verifies that
|
|
2384
|
+
* `verifyingContract` matches the requesting origin.
|
|
2385
|
+
* 2. Derivation layer (`PrivacyKeys`) — the HKDF `info` carries the version, so
|
|
2386
|
+
* v1 and v2 are disjoint identities even given identical signature bytes.
|
|
2387
|
+
* This is the layer that holds when layer 1 fails (e.g. a wallet that
|
|
2388
|
+
* truncates the warning text).
|
|
2389
|
+
*
|
|
2390
|
+
* DETERMINISM IS MANDATORY. No builder may add a nonce, timestamp or challenge:
|
|
2391
|
+
* the digest must stay a pure function of (chainId, address). A signature that
|
|
2392
|
+
* varies per session yields a different spending key each time, which makes
|
|
2393
|
+
* already-shielded notes unspendable — surfacing as an opaque Merkle constraint
|
|
2394
|
+
* failure during witness generation.
|
|
2395
|
+
*/
|
|
2396
|
+
/**
|
|
2397
|
+
* EIP-712 `verifyingContract` for spending-key derivation: the shielded pool
|
|
2398
|
+
* precompile, i.e. the component that actually custodies shielded funds and so
|
|
2399
|
+
* the semantically correct domain anchor.
|
|
2400
|
+
*
|
|
2401
|
+
* WARNING: this value is part of the EIP-712 digest. Changing it changes every
|
|
2402
|
+
* derived spending key and orphans every existing note. It is a protocol
|
|
2403
|
+
* constant, not a config knob — which is why it is re-exported from the single
|
|
2404
|
+
* `PRECOMPILE_ADDR` source rather than written out a second time.
|
|
2405
|
+
*/
|
|
2406
|
+
declare const SPENDING_KEY_VERIFYING_CONTRACT: "0x0000000000000000000000000000000000000801";
|
|
2407
|
+
/**
|
|
2408
|
+
* Shown to the user inside the wallet prompt. Wallets render EIP-712 message
|
|
2409
|
+
* fields (and Substrate raw text) verbatim, so this is the one surface a hostile
|
|
2410
|
+
* origin can neither suppress nor reword.
|
|
2411
|
+
*/
|
|
2412
|
+
declare const SPENDING_KEY_WARNING: string;
|
|
2413
|
+
/** EIP-712 payload for `eth_signTypedData_v4`. */
|
|
2414
|
+
interface SpendingKeyTypedData {
|
|
2415
|
+
domain: {
|
|
2416
|
+
name: string;
|
|
2417
|
+
version: string;
|
|
2418
|
+
chainId: number;
|
|
2419
|
+
verifyingContract: string;
|
|
2420
|
+
};
|
|
2421
|
+
types: {
|
|
2422
|
+
SpendingKeyDerivation: ReadonlyArray<{
|
|
2423
|
+
name: string;
|
|
2424
|
+
type: string;
|
|
2425
|
+
}>;
|
|
2426
|
+
};
|
|
2427
|
+
primaryType: 'SpendingKeyDerivation';
|
|
2428
|
+
message: {
|
|
2429
|
+
warning: string;
|
|
2430
|
+
account: string;
|
|
2431
|
+
};
|
|
2432
|
+
}
|
|
2433
|
+
/**
|
|
2434
|
+
* EIP-712 typed data the user signs to derive their spending key (EVM route).
|
|
2435
|
+
* Pass the result to `eth_signTypedData_v4`.
|
|
2436
|
+
*
|
|
2437
|
+
* @param chainId Chain the identity belongs to; part of the domain separator.
|
|
2438
|
+
* @param address Signer address. Lowercased so checksum casing cannot fork the
|
|
2439
|
+
* identity into two distinct keys for the same account.
|
|
2440
|
+
*/
|
|
2441
|
+
declare function deriveSpendingKeyTypedData(chainId: number, address: string): SpendingKeyTypedData;
|
|
2442
|
+
/**
|
|
2443
|
+
* Message the user signs on signers without EIP-712 (Substrate: sr25519 via VRF,
|
|
2444
|
+
* ed25519 via signRaw).
|
|
2445
|
+
*
|
|
2446
|
+
* Substrate wallets render the raw string, so the warning leads the text — it is
|
|
2447
|
+
* the only channel a malicious extension cannot rewrite. Its protection is
|
|
2448
|
+
* therefore conditional on the wallet not truncating the message; when it does,
|
|
2449
|
+
* only the HKDF domain separation in `PrivacyKeys` still applies.
|
|
2450
|
+
*/
|
|
2451
|
+
declare function deriveSpendingKeyMessageV2(chainId: number, address: string): string;
|
|
2452
|
+
/**
|
|
2453
|
+
* @deprecated INSECURE — v1 derivation. Signs a fixed public string via
|
|
2454
|
+
* `personal_sign`, so any dapp can request it and, because the signature is
|
|
2455
|
+
* deterministic, reconstruct the user's spending key, viewing key and vault key.
|
|
2456
|
+
*
|
|
2457
|
+
* Retained ONLY so existing v1 notes can be swept into a v2 identity. Never call
|
|
2458
|
+
* it on a connect/login path. Use {@link deriveSpendingKeyTypedData} (EVM) or
|
|
2459
|
+
* {@link deriveSpendingKeyMessageV2} (Substrate) instead.
|
|
2460
|
+
*/
|
|
2461
|
+
declare function deriveSpendingKeyMessage(chainId: number, address: string): string;
|
|
2462
|
+
|
|
2321
2463
|
/**
|
|
2322
2464
|
* PrivacyKeys
|
|
2323
2465
|
*
|
|
2324
|
-
* Pure cryptographic derivation
|
|
2325
|
-
*
|
|
2466
|
+
* Pure cryptographic derivation for the Orbinum shielded pool identity: turns a
|
|
2467
|
+
* wallet signature into key material, and key material into the public values
|
|
2468
|
+
* that make up a privacy address. Protocol-level only — no storage, UI or
|
|
2469
|
+
* session concerns. What the user *signs* to produce that signature lives in
|
|
2470
|
+
* `SpendingKeyRequest`.
|
|
2471
|
+
*
|
|
2472
|
+
* Full derivation chain:
|
|
2326
2473
|
*
|
|
2327
|
-
*
|
|
2328
|
-
*
|
|
2329
|
-
*
|
|
2330
|
-
*
|
|
2331
|
-
*
|
|
2474
|
+
* signature ──HKDF(info="orbinum-sk-{version}:{chainId}:{address}")──► masterBytes (32B)
|
|
2475
|
+
* │
|
|
2476
|
+
* ┌─────────────────────────────────────────────────────────────────┤
|
|
2477
|
+
* ▼ ▼
|
|
2478
|
+
* spendingKey = BigInt(masterBytes) % BABYJUB_SUBORDER vaultKey (see vault/)
|
|
2479
|
+
* │ = HKDF(masterBytes, "orbinum-vault-key-v1")
|
|
2480
|
+
* ├──► ownerPk = BJJ_mul(Base8, spendingKey).Ax (public)
|
|
2481
|
+
* │
|
|
2482
|
+
* └──► ivsk = HKDF(LE32(spendingKey), info="orbinum-ivk-v1") (secret)
|
|
2483
|
+
* └──► ivk = packPoint(BJJ_mul(Base8, ivsk_scalar)) (public)
|
|
2332
2484
|
*
|
|
2333
|
-
*
|
|
2334
|
-
*
|
|
2335
|
-
*
|
|
2336
|
-
*
|
|
2485
|
+
* VERSIONING: the HKDF `info` carries the identity version, so v1 and v2 are
|
|
2486
|
+
* cryptographically disjoint even given identical signature bytes. This is the
|
|
2487
|
+
* layer that still separates the identities when the message-level defense fails
|
|
2488
|
+
* — see the security model in `SpendingKeyRequest`.
|
|
2337
2489
|
*
|
|
2338
|
-
*
|
|
2339
|
-
* Num2Bits(253)
|
|
2340
|
-
*
|
|
2490
|
+
* MODULUS: reduce mod BABYJUB_SUBORDER, never BN254_R. circomlib's BabyPbk uses
|
|
2491
|
+
* Num2Bits(253), asserting spending_key < 2^253. BABYJUB_SUBORDER < 2^252
|
|
2492
|
+
* satisfies it; BN254_R ≈ 2^254.8 does not — ~34% of values would fail at runtime.
|
|
2341
2493
|
*/
|
|
2494
|
+
/** Identity version. `v1` is the legacy personal_sign scheme, kept for sweeping only. */
|
|
2495
|
+
type KeyVersion = 'v1' | 'v2';
|
|
2342
2496
|
/**
|
|
2343
|
-
*
|
|
2344
|
-
*
|
|
2497
|
+
* Shortest signature any supported signer produces: sr25519 VRF output is 32
|
|
2498
|
+
* bytes, ed25519 is 64, ECDSA `personal_sign` is 65. Anything shorter is not a
|
|
2499
|
+
* signature, so it must never reach the KDF.
|
|
2345
2500
|
*/
|
|
2346
|
-
declare
|
|
2501
|
+
declare const MIN_SIGNATURE_BYTES = 32;
|
|
2347
2502
|
/**
|
|
2348
2503
|
* Derives the 32-byte master key bytes from a wallet signature.
|
|
2349
2504
|
*
|
|
2350
|
-
* masterBytes = HKDF-SHA256(ikm=sigBytes, salt=empty, info="orbinum-sk-
|
|
2505
|
+
* masterBytes = HKDF-SHA256(ikm=sigBytes, salt=empty, info="orbinum-sk-{version}:{chainId}:{address}")
|
|
2351
2506
|
*
|
|
2352
2507
|
* These bytes are the stable root for ALL derived keys:
|
|
2353
2508
|
* - spendingKey (circuit scalar) = BigInt(masterBytes) % BABYJUB_SUBORDER
|
|
@@ -2357,12 +2512,19 @@ declare function deriveSpendingKeyMessage(chainId: number, address: string): str
|
|
|
2357
2512
|
* Separating masterBytes from the circuit scalar means the viewingSecretKey and
|
|
2358
2513
|
* vault key are STABLE across any future change to the modulus — they never
|
|
2359
2514
|
* depend on which prime field the circuit uses.
|
|
2515
|
+
*
|
|
2516
|
+
* The version is folded into the HKDF `info`, so v1 and v2 stay disjoint even if
|
|
2517
|
+
* the underlying signature bytes were somehow identical.
|
|
2518
|
+
*
|
|
2519
|
+
* @param version Defaults to 'v2'. Pass 'v1' only from the legacy sweep flow.
|
|
2520
|
+
* @throws If the signature is not valid hex, or carries less entropy than the
|
|
2521
|
+
* shortest real signing scheme (see {@link MIN_SIGNATURE_BYTES}).
|
|
2360
2522
|
*/
|
|
2361
|
-
declare function deriveMasterKeyBytes(signatureHex: string, chainId: number, address: string): Promise<Uint8Array>;
|
|
2523
|
+
declare function deriveMasterKeyBytes(signatureHex: string, chainId: number, address: string, version?: KeyVersion): Promise<Uint8Array>;
|
|
2362
2524
|
/**
|
|
2363
2525
|
* Derives an Orbinum spending key from a wallet signature.
|
|
2364
2526
|
*
|
|
2365
|
-
* Uses HKDF-SHA256(ikm=sigBytes, salt=empty, info="orbinum-sk-
|
|
2527
|
+
* Uses HKDF-SHA256(ikm=sigBytes, salt=empty, info="orbinum-sk-{version}:{chainId}:{address}")
|
|
2366
2528
|
* and reduces the resulting 32-byte value modulo BABYJUB_SUBORDER.
|
|
2367
2529
|
*
|
|
2368
2530
|
* IMPORTANT: viewingSecretKey and vaultKey must be derived from masterBytes (via
|
|
@@ -2372,9 +2534,12 @@ declare function deriveMasterKeyBytes(signatureHex: string, chainId: number, add
|
|
|
2372
2534
|
* @param signatureHex 0x-prefixed or bare hex of the wallet signature.
|
|
2373
2535
|
* @param chainId Chain ID used when building the signing message.
|
|
2374
2536
|
* @param address Signer address (EVM or SS58) used in the signing message.
|
|
2537
|
+
* @param version Defaults to 'v2'. Pass 'v1' only from the legacy sweep flow.
|
|
2375
2538
|
* @returns bigint in [1, BABYJUB_SUBORDER)
|
|
2539
|
+
* @throws If the signature is not valid hex or is shorter than
|
|
2540
|
+
* {@link MIN_SIGNATURE_BYTES} — see `deriveMasterKeyBytes`.
|
|
2376
2541
|
*/
|
|
2377
|
-
declare function deriveSpendingKeyFromSignature(signatureHex: string, chainId: number, address: string): Promise<bigint>;
|
|
2542
|
+
declare function deriveSpendingKeyFromSignature(signatureHex: string, chainId: number, address: string, version?: KeyVersion): Promise<bigint>;
|
|
2378
2543
|
/**
|
|
2379
2544
|
* Derive a 32-byte viewing secret key (ivsk) from the spending key.
|
|
2380
2545
|
* ivsk = HKDF-SHA256(ikm=bigintTo32Le(spendingKey), info="orbinum-ivk-v1")
|
|
@@ -4269,4 +4434,4 @@ interface ExtrinsicFailedData {
|
|
|
4269
4434
|
dispatch_info: DispatchInfo;
|
|
4270
4435
|
}
|
|
4271
4436
|
|
|
4272
|
-
export { type AccountListing, type AccountMappedData, type AccountMappedEvent, type AccountMappingCall, type AccountMappingEvent, AccountMappingModule, AccountMappingPrecompile, type AccountMetadata, type AccountUnmappedEvent, type ActiveVersionSetEvent, type AddChainLinkArgs, type AddChainLinkParams, type AddSupportedChainArgs, type AliasFullIdentity, type AliasInfo, type AliasListedForSaleEvent, type AliasOnSaleData, type AliasRegisteredData, type AliasRegisteredEvent, type AliasReleasedEvent, type AliasSaleCancelledEvent, type AliasSoldData, type AliasSoldEvent, type AliasTransferredData, type AliasTransferredEvent, type AssetRegisteredEvent, type AssetUnverifiedEvent, type AssetVerifiedEvent, BABYJUB_SUBORDER, BN254_R, type BatchRegisterVerificationKeysArgs, type BatchVerificationKeysRegisteredEvent, type BlockInfo, type BuyAliasArgs, type Bytes32, CURRENT_CIRCUIT_VERSION, type ChainInfo, type ChainLink, type ChainLinkAddedEvent, type ChainLinkRemovedEvent, CircuitId, CircuitId as CircuitIdType, CircuitVersionResolver, type ClaimShieldedFeesParams, type ClientProviderConfig, type CommitmentsInsertedEvent, type CommitmentsInsertedEventData, type ConnectionStatus, CryptoPrecompiles, type DecodedAddChainLinkArgs, type DecodedBatchArgs, type DecodedDispatchAsPrivateLinkArgs, type DecodedEthereumTransactArgs, type DecodedEvmCallArgs, type DecodedPrecompile, type DecodedPrivateTransferArgs, type DecodedPutAliasForSaleArgs, type DecodedRegisterAliasArgs, type DecodedRemarkArgs, type DecodedRevealPrivateLinkArgs, type DecodedSetAccountMetadataArgs, type DecodedShieldArgs, type DecodedShieldBatchArgs, type DecodedShieldBatchOperation, type DecodedSudoArgs, type DecodedTransferAllArgs, type DecodedTransferArgs, type DecodedTransferKeepAliveArgs, type DecodedUnshieldArgs, type DecryptedMemo, type DispatchAsLinkedAccountArgs, type DispatchAsLinkedParams, type DispatchAsPrivateLinkArgs, type DispatchError, type DispatchInfo, type DynamicBuilder, ENCRYPTED_MEMO_SIZE, EncryptedMemo, type EncryptedNoteRecord, type EndowedEventData, type EthereumExecutedData, type EventData, type EventPhase, type EventRecord, type EvmAddressInfo, type EvmBlock, EvmClient, type EvmExecutedData, type EvmExitReason, EvmExplorer, type EvmLog, type EvmSigner, type EvmTransaction, type EvmTxRequest, type EvmTxSummary, type ExtrinsicDecoder, type ExtrinsicFailedData, type ExtrinsicSuccessData, type FeeClaimProofInputs, type FormatOptions, KNOWN_PRECOMPILES, type KnownPrecompileInfo, type ListingInfo, type MerkleRootUpdatedData, type MerkleRootUpdatedEvent, type MerkleTreeInfo, type MetadataUpdatedEvent, NoteBuilder, type NoteDisclosure, type NoteInput, type NoteStatusUpdate, type NullifiersSpentEvent, type NullifiersSpentEventData, OrbinumClient, type OrbinumClientConfig, OrbinumClientProvider, PRECOMPILE_ADDR, PrivacyKeyManager, type PrivacyMerkleProof, PrivacyModule, type PrivateChainLinkAddedEvent, type PrivateChainLinkRemovedEvent, type PrivateChainLinkRevealedEvent, type PrivateLink, type PrivateLinkDispatchExecutedEvent, type PrivateTransferArgs, type PrivateTransferInput, type PrivateTransferOutput, type PrivateTransferParams, type PrivateTransferProofInputs, type ProofVerificationFailedEvent, type ProofVerifiedEvent, type ProxyCallExecutedEvent, type PutAliasOnSaleArgs, type PutOnSaleParams, type RawBlock, type RawBlockHeader, type RawTransferInput, type RawTransferOutput, type RegisterAliasArgs, type RegisterAssetArgs, type RegisterPrivateLinkArgs, type RegisterVerificationKeyArgs, type RelayerInfo, RelayerStatusModule, type RemoveChainLinkArgs, type RemovePrivateLinkArgs, type RemoveSupportedChainArgs, type RemoveVerificationKeyArgs, type ReservedEventData, type ResolvedAlias, type ResolvedSpendVersion, type RevealPrivateLinkArgs, type RpcV2MerkleProof, type RpcV2NullifierStatus, type RpcV2PoolAssetBalance, type RpcV2PoolStats, SLIP0044_NAMESPACE, type ScanCommitment, type SelfEphWindowEntry, type SetAccountMetadataArgs, type SetActiveVersionArgs, type SetMetadataParams, type ShieldArgs, type ShieldBatchArgs, type ShieldBatchItem, type ShieldBatchParams, type ShieldOperation, type ShieldParams, type ShieldedEvent, type ShieldedEventData, type ShieldedPoolCall, type ShieldedPoolEvent, ShieldedPoolModule, ShieldedPoolPrecompile, SignatureScheme, type StatusChangeEvent, type StatusListener, SubstrateClient, type SupportedChain, type SupportedChainAddedEvent, type SupportedChainRemovedEvent, type SystemHealth, type TokenInfo, type TokenTransfer, type TransferAliasArgs, type TransferEventData, type TransferInputNote, type TransferOutputNote, type TryDecryptOptions, type TxResult, type UnsafeTxOptions, type UnshieldArgs, type UnshieldParams, type UnshieldProofInputs, type UnshieldedEvent, type UnshieldedEventData, type UnverifyAssetArgs, VaultLockedError, type VerificationKeyRegisteredEvent, type VerificationKeyRemovedEvent, type VerifyAssetArgs, type VerifyProofArgs, type VkEntry, type ZkNote, type ZkVerifierCall, type ZkVerifierCircuitVersionInfo, type ZkVerifierEvent, type ZkVerifierHistoricalVersion, ZkVerifierModule, type ZkVerifierVersionStats, type ZkVerifierVkHash, accountIdHexToSs58, addressToAccountIdHex, applyNoteStatus, bigintTo32Be, bigintTo32Le, bigintTo32LeArr, blindTag, buildDummyTransferInput, bytesToBigintLE, computeNoteCommitment, computeNullifier, computePathIndices, createNoteDisclosureKey, decodeNoteDisclosureKey, decodePrecompileCalldata, decryptJson, decryptNoteRecord, deriveMasterKeyBytes, deriveOwnerPk, deriveSelfEphSk, deriveSpendingKeyFromSignature, deriveSpendingKeyMessage, deriveStealthOwnerPk, deriveStealthSk, deriveVaultBlindKey, deriveVaultKey, deriveViewTag, deriveViewingPublicKey, deriveViewingSecretKey, encryptJson, encryptNote, ensureHexPrefix, evmAddressToAccountId, evmToImplicitSubstrate, evmToMappedAccountHex, evmToSubstrate, formatBalance, formatORB, fromBase64, fromHex, generateFeeClaimProof, generateTransferProof, generateUnshieldProof, getPrecompileLabel, hexToBigint, hexToNumber, implicitSubstrateToEvm, isEvmAddress, isImplicitEvmAccount, isSs58, isSubstrateAddress, isUnifiedAddress, leHexToBigint, mapExtrinsicArgs, mapZkEventData, normalizeEvmAddress, noteBlindTag, randomBlinding, recoverOwnerPkPoint, selectNotes, selfEphWindow, shortHash, substrateSs58ToAccountIdHex, substrateToEvm, toBase64, toHex, toTxResult, truncateMiddle, tryDecryptNote, tryDecryptNoteVerbose, vaultReplacer, vaultReviver };
|
|
4437
|
+
export { type AccountListing, type AccountMappedData, type AccountMappedEvent, type AccountMappingCall, type AccountMappingEvent, AccountMappingModule, AccountMappingPrecompile, type AccountMetadata, type AccountUnmappedEvent, type ActiveVersionSetEvent, type AddChainLinkArgs, type AddChainLinkParams, type AddSupportedChainArgs, type AliasFullIdentity, type AliasInfo, type AliasListedForSaleEvent, type AliasOnSaleData, type AliasRegisteredData, type AliasRegisteredEvent, type AliasReleasedEvent, type AliasSaleCancelledEvent, type AliasSoldData, type AliasSoldEvent, type AliasTransferredData, type AliasTransferredEvent, type AssetRegisteredEvent, type AssetUnverifiedEvent, type AssetVerifiedEvent, BABYJUB_SUBORDER, BN254_R, type BatchRegisterVerificationKeysArgs, type BatchVerificationKeysRegisteredEvent, type BlockInfo, type BuyAliasArgs, type Bytes32, CURRENT_CIRCUIT_VERSION, type ChainInfo, type ChainLink, type ChainLinkAddedEvent, type ChainLinkRemovedEvent, CircuitId, CircuitId as CircuitIdType, CircuitVersionResolver, type ClaimShieldedFeesParams, type ClientProviderConfig, type CommitmentsInsertedEvent, type CommitmentsInsertedEventData, type ConnectionStatus, CryptoPrecompiles, type DecodedAddChainLinkArgs, type DecodedBatchArgs, type DecodedDispatchAsPrivateLinkArgs, type DecodedEthereumTransactArgs, type DecodedEvmCallArgs, type DecodedPrecompile, type DecodedPrivateTransferArgs, type DecodedPutAliasForSaleArgs, type DecodedRegisterAliasArgs, type DecodedRemarkArgs, type DecodedRevealPrivateLinkArgs, type DecodedSetAccountMetadataArgs, type DecodedShieldArgs, type DecodedShieldBatchArgs, type DecodedShieldBatchOperation, type DecodedSudoArgs, type DecodedTransferAllArgs, type DecodedTransferArgs, type DecodedTransferKeepAliveArgs, type DecodedUnshieldArgs, type DecryptedMemo, type DispatchAsLinkedAccountArgs, type DispatchAsLinkedParams, type DispatchAsPrivateLinkArgs, type DispatchError, type DispatchInfo, type DynamicBuilder, ENCRYPTED_MEMO_SIZE, EncryptedMemo, type EncryptedNoteRecord, type EndowedEventData, type EthereumExecutedData, type EventData, type EventPhase, type EventRecord, type EvmAddressInfo, type EvmBlock, EvmClient, type EvmExecutedData, type EvmExitReason, EvmExplorer, type EvmLog, type EvmSigner, type EvmTransaction, type EvmTxRequest, type EvmTxSummary, type ExtrinsicDecoder, type ExtrinsicFailedData, type ExtrinsicSuccessData, type FeeClaimProofInputs, type FormatOptions, KNOWN_PRECOMPILES, type KeyVersion, type KnownPrecompileInfo, type ListingInfo, MIN_SIGNATURE_BYTES, type MerkleRootUpdatedData, type MerkleRootUpdatedEvent, type MerkleTreeInfo, type MetadataUpdatedEvent, NoteBuilder, type NoteDisclosure, type NoteInput, type NoteStatusUpdate, type NullifiersSpentEvent, type NullifiersSpentEventData, OrbinumClient, type OrbinumClientConfig, OrbinumClientProvider, PRECOMPILE_ADDR, PrivacyKeyManager, type PrivacyMerkleProof, PrivacyModule, type PrivateChainLinkAddedEvent, type PrivateChainLinkRemovedEvent, type PrivateChainLinkRevealedEvent, type PrivateLink, type PrivateLinkDispatchExecutedEvent, type PrivateTransferArgs, type PrivateTransferInput, type PrivateTransferOutput, type PrivateTransferParams, type PrivateTransferProofInputs, type ProofVerificationFailedEvent, type ProofVerifiedEvent, type ProxyCallExecutedEvent, type PutAliasOnSaleArgs, type PutOnSaleParams, type RawBlock, type RawBlockHeader, type RawTransferInput, type RawTransferOutput, type RegisterAliasArgs, type RegisterAssetArgs, type RegisterPrivateLinkArgs, type RegisterVerificationKeyArgs, type RelayerInfo, RelayerStatusModule, type RemoveChainLinkArgs, type RemovePrivateLinkArgs, type RemoveSupportedChainArgs, type RemoveVerificationKeyArgs, type ReservedEventData, type ResolvedAlias, type ResolvedSpendVersion, type RevealPrivateLinkArgs, type RpcV2MerkleProof, type RpcV2NullifierStatus, type RpcV2PoolAssetBalance, type RpcV2PoolStats, SLIP0044_NAMESPACE, SPENDING_KEY_VERIFYING_CONTRACT, SPENDING_KEY_WARNING, type ScanCommitment, type SelfEphWindowEntry, type SetAccountMetadataArgs, type SetActiveVersionArgs, type SetMetadataParams, type ShieldArgs, type ShieldBatchArgs, type ShieldBatchItem, type ShieldBatchParams, type ShieldOperation, type ShieldParams, type ShieldedEvent, type ShieldedEventData, type ShieldedPoolCall, type ShieldedPoolEvent, ShieldedPoolModule, ShieldedPoolPrecompile, SignatureScheme, type SpendingKeyTypedData, type StatusChangeEvent, type StatusListener, SubstrateClient, type SupportedChain, type SupportedChainAddedEvent, type SupportedChainRemovedEvent, type SystemHealth, type TokenInfo, type TokenTransfer, type TransferAliasArgs, type TransferEventData, type TransferInputNote, type TransferOutputNote, type TryDecryptOptions, type TxResult, type UnsafeTxOptions, type UnshieldArgs, type UnshieldParams, type UnshieldProofInputs, type UnshieldedEvent, type UnshieldedEventData, type UnverifyAssetArgs, VaultLockedError, type VerificationKeyRegisteredEvent, type VerificationKeyRemovedEvent, type VerifyAssetArgs, type VerifyProofArgs, type VkEntry, type ZkNote, type ZkVerifierCall, type ZkVerifierCircuitVersionInfo, type ZkVerifierEvent, type ZkVerifierHistoricalVersion, ZkVerifierModule, type ZkVerifierVersionStats, type ZkVerifierVkHash, accountIdHexToSs58, addressToAccountIdHex, applyNoteStatus, bigintTo32Be, bigintTo32Le, bigintTo32LeArr, blindTag, buildDummyTransferInput, bytesToBigintLE, computeNoteCommitment, computeNullifier, computePathIndices, createNoteDisclosureKey, decodeNoteDisclosureKey, decodePrecompileCalldata, decryptJson, decryptNoteRecord, deriveMasterKeyBytes, deriveOwnerPk, deriveSelfEphSk, deriveSpendingKeyFromSignature, deriveSpendingKeyMessage, deriveSpendingKeyMessageV2, deriveSpendingKeyTypedData, deriveStealthOwnerPk, deriveStealthSk, deriveVaultBlindKey, deriveVaultKey, deriveViewTag, deriveViewingPublicKey, deriveViewingSecretKey, encryptJson, encryptNote, ensureHexPrefix, evmAddressToAccountId, evmToImplicitSubstrate, evmToMappedAccountHex, evmToSubstrate, formatBalance, formatORB, fromBase64, fromHex, generateFeeClaimProof, generateTransferProof, generateUnshieldProof, getPrecompileLabel, hexToBigint, hexToNumber, implicitSubstrateToEvm, isEvmAddress, isImplicitEvmAccount, isSs58, isSubstrateAddress, isUnifiedAddress, leHexToBigint, mapExtrinsicArgs, mapZkEventData, normalizeEvmAddress, noteBlindTag, randomBlinding, recoverOwnerPkPoint, selectNotes, selfEphWindow, shortHash, substrateSs58ToAccountIdHex, substrateToEvm, toBase64, toHex, toTxResult, truncateMiddle, tryDecryptNote, tryDecryptNoteVerbose, vaultReplacer, vaultReviver };
|
package/dist/index.d.ts
CHANGED
|
@@ -105,6 +105,18 @@ declare class SubstrateClient {
|
|
|
105
105
|
private constructor();
|
|
106
106
|
private _dynamicBuilder;
|
|
107
107
|
private _extDecoder;
|
|
108
|
+
private _inflightTxCount;
|
|
109
|
+
/**
|
|
110
|
+
* `true` while any submitted transaction is still waiting for finalization.
|
|
111
|
+
* Connection managers use this to defer destroying the client — killing the
|
|
112
|
+
* WS mid-submit rejects the pending tx with "Client destroyed" even though
|
|
113
|
+
* it may still land on-chain.
|
|
114
|
+
*
|
|
115
|
+
* Only covers promise-based submits (`submit`, `submitUnsignedAndWatch`,
|
|
116
|
+
* `signAndSubmit`); observable-based `submitAndWatch` callers are not tracked.
|
|
117
|
+
*/
|
|
118
|
+
get hasInflightTx(): boolean;
|
|
119
|
+
private trackTx;
|
|
108
120
|
/**
|
|
109
121
|
* Connects to the Orbinum node via WebSocket.
|
|
110
122
|
* Throws if the node does not respond within `timeoutMs`.
|
|
@@ -285,14 +297,25 @@ declare class EvmClient {
|
|
|
285
297
|
}): Promise<bigint>;
|
|
286
298
|
/** Returns a transaction receipt by hash, or `null` if the transaction has not been mined yet. */
|
|
287
299
|
getTransactionReceipt(txHash: string): Promise<Record<string, unknown> | null>;
|
|
300
|
+
/**
|
|
301
|
+
* Fetches a transaction by hash, or `null` when the node no longer knows it
|
|
302
|
+
* (never mined and evicted from the pool). Unlike `request`, a `null`
|
|
303
|
+
* result is a valid answer here, not an error.
|
|
304
|
+
*/
|
|
305
|
+
getTransactionByHash(txHash: string): Promise<Record<string, unknown> | null>;
|
|
288
306
|
/**
|
|
289
307
|
* Polls `eth_getTransactionReceipt` until the transaction is included in a block.
|
|
290
308
|
*
|
|
309
|
+
* After `timeoutMs`, the tx-pool is consulted: a tx no longer known to the
|
|
310
|
+
* node is reported as dropped (safe to retry), while a tx still in the pool
|
|
311
|
+
* gets an extended grace window (up to 4× `timeoutMs`) before a "still
|
|
312
|
+
* pending" error — it may confirm later, so callers must NOT blindly retry.
|
|
313
|
+
*
|
|
291
314
|
* @param txHash - The transaction hash to wait for.
|
|
292
315
|
* @param intervalMs - Polling interval in milliseconds (default: 500).
|
|
293
316
|
* @param timeoutMs - Maximum time to wait in milliseconds (default: 60_000).
|
|
294
317
|
* @returns The transaction receipt once mined.
|
|
295
|
-
* @throws If the transaction is
|
|
318
|
+
* @throws If the transaction dropped, is still pending after the grace window, or reverted (`status == 0x0`).
|
|
296
319
|
*/
|
|
297
320
|
waitForReceipt(txHash: string, intervalMs?: number, timeoutMs?: number): Promise<Record<string, unknown>>;
|
|
298
321
|
}
|
|
@@ -1776,6 +1799,7 @@ declare class OrbinumClientProvider {
|
|
|
1776
1799
|
private _reconnectTimer;
|
|
1777
1800
|
private _stableTimer;
|
|
1778
1801
|
private _reconnectAttempt;
|
|
1802
|
+
private _probeFailures;
|
|
1779
1803
|
private _listeners;
|
|
1780
1804
|
/** Creates a new provider with the given configuration. Does not connect automatically — call `connect()` to initiate. */
|
|
1781
1805
|
constructor(config: ClientProviderConfig);
|
|
@@ -1806,6 +1830,21 @@ declare class OrbinumClientProvider {
|
|
|
1806
1830
|
* On failure: destroys any orphaned client and transitions to `'disconnected'`.
|
|
1807
1831
|
*/
|
|
1808
1832
|
private attemptConnect;
|
|
1833
|
+
/**
|
|
1834
|
+
* Consecutive failed probes required before the client is torn down. A
|
|
1835
|
+
* single missed probe is routine (throttled background tab, node busy
|
|
1836
|
+
* verifying a ZK proof, transient network blip) — destroying the client on
|
|
1837
|
+
* it rejects every in-flight request with "Client destroyed" even though
|
|
1838
|
+
* the tx may still land on-chain.
|
|
1839
|
+
*/
|
|
1840
|
+
private static readonly PROBE_FAILURE_THRESHOLD;
|
|
1841
|
+
/**
|
|
1842
|
+
* With a tx awaiting finalization, tolerate more missed probes: an unsigned
|
|
1843
|
+
* private_transfer/unshield makes the node CPU-bound on proof verification,
|
|
1844
|
+
* which is exactly when probes time out — tearing down then kills the very
|
|
1845
|
+
* tx being processed.
|
|
1846
|
+
*/
|
|
1847
|
+
private static readonly PROBE_FAILURE_THRESHOLD_INFLIGHT;
|
|
1809
1848
|
/** Starts the periodic heartbeat loop. Replaces any existing timer. */
|
|
1810
1849
|
private startHeartbeat;
|
|
1811
1850
|
/** Clears the heartbeat interval timer if active. */
|
|
@@ -2318,36 +2357,152 @@ declare const BABYJUB_SUBORDER = 27360303589799094027808007181571593860768139721
|
|
|
2318
2357
|
*/
|
|
2319
2358
|
declare function randomBlinding(): bigint;
|
|
2320
2359
|
|
|
2360
|
+
/**
|
|
2361
|
+
* SpendingKeyRequest
|
|
2362
|
+
*
|
|
2363
|
+
* What the user signs to derive their Orbinum spending key, per platform.
|
|
2364
|
+
*
|
|
2365
|
+
* These builders produce the *request* (typed data or message string). Turning a
|
|
2366
|
+
* signature into key material lives in `PrivacyKeys` — the split keeps the
|
|
2367
|
+
* platform-specific presentation away from the platform-agnostic KDF.
|
|
2368
|
+
*
|
|
2369
|
+
* SECURITY MODEL
|
|
2370
|
+
*
|
|
2371
|
+
* The signature seeds the spending key, so a deterministic signature over a
|
|
2372
|
+
* fixed, public string is a bearer token: any dapp the user connects to can
|
|
2373
|
+
* request the same string, obtain a byte-identical signature (ECDSA personal_sign
|
|
2374
|
+
* is deterministic, RFC-6979) and reconstruct the spending key, viewing key and
|
|
2375
|
+
* vault key. That is the v1 flaw these builders exist to close.
|
|
2376
|
+
*
|
|
2377
|
+
* The defense has two independent layers:
|
|
2378
|
+
*
|
|
2379
|
+
* 1. Message layer (here) — bind the signature to a domain the user can see.
|
|
2380
|
+
* EVM uses EIP-712, whose domain the wallet renders. Substrate has no
|
|
2381
|
+
* EIP-712, so the warning travels inside the signed text instead. NOTE this
|
|
2382
|
+
* layer is about VISIBILITY, not impossibility: nothing stops a hostile
|
|
2383
|
+
* origin from requesting the same payload, and no wallet verifies that
|
|
2384
|
+
* `verifyingContract` matches the requesting origin.
|
|
2385
|
+
* 2. Derivation layer (`PrivacyKeys`) — the HKDF `info` carries the version, so
|
|
2386
|
+
* v1 and v2 are disjoint identities even given identical signature bytes.
|
|
2387
|
+
* This is the layer that holds when layer 1 fails (e.g. a wallet that
|
|
2388
|
+
* truncates the warning text).
|
|
2389
|
+
*
|
|
2390
|
+
* DETERMINISM IS MANDATORY. No builder may add a nonce, timestamp or challenge:
|
|
2391
|
+
* the digest must stay a pure function of (chainId, address). A signature that
|
|
2392
|
+
* varies per session yields a different spending key each time, which makes
|
|
2393
|
+
* already-shielded notes unspendable — surfacing as an opaque Merkle constraint
|
|
2394
|
+
* failure during witness generation.
|
|
2395
|
+
*/
|
|
2396
|
+
/**
|
|
2397
|
+
* EIP-712 `verifyingContract` for spending-key derivation: the shielded pool
|
|
2398
|
+
* precompile, i.e. the component that actually custodies shielded funds and so
|
|
2399
|
+
* the semantically correct domain anchor.
|
|
2400
|
+
*
|
|
2401
|
+
* WARNING: this value is part of the EIP-712 digest. Changing it changes every
|
|
2402
|
+
* derived spending key and orphans every existing note. It is a protocol
|
|
2403
|
+
* constant, not a config knob — which is why it is re-exported from the single
|
|
2404
|
+
* `PRECOMPILE_ADDR` source rather than written out a second time.
|
|
2405
|
+
*/
|
|
2406
|
+
declare const SPENDING_KEY_VERIFYING_CONTRACT: "0x0000000000000000000000000000000000000801";
|
|
2407
|
+
/**
|
|
2408
|
+
* Shown to the user inside the wallet prompt. Wallets render EIP-712 message
|
|
2409
|
+
* fields (and Substrate raw text) verbatim, so this is the one surface a hostile
|
|
2410
|
+
* origin can neither suppress nor reword.
|
|
2411
|
+
*/
|
|
2412
|
+
declare const SPENDING_KEY_WARNING: string;
|
|
2413
|
+
/** EIP-712 payload for `eth_signTypedData_v4`. */
|
|
2414
|
+
interface SpendingKeyTypedData {
|
|
2415
|
+
domain: {
|
|
2416
|
+
name: string;
|
|
2417
|
+
version: string;
|
|
2418
|
+
chainId: number;
|
|
2419
|
+
verifyingContract: string;
|
|
2420
|
+
};
|
|
2421
|
+
types: {
|
|
2422
|
+
SpendingKeyDerivation: ReadonlyArray<{
|
|
2423
|
+
name: string;
|
|
2424
|
+
type: string;
|
|
2425
|
+
}>;
|
|
2426
|
+
};
|
|
2427
|
+
primaryType: 'SpendingKeyDerivation';
|
|
2428
|
+
message: {
|
|
2429
|
+
warning: string;
|
|
2430
|
+
account: string;
|
|
2431
|
+
};
|
|
2432
|
+
}
|
|
2433
|
+
/**
|
|
2434
|
+
* EIP-712 typed data the user signs to derive their spending key (EVM route).
|
|
2435
|
+
* Pass the result to `eth_signTypedData_v4`.
|
|
2436
|
+
*
|
|
2437
|
+
* @param chainId Chain the identity belongs to; part of the domain separator.
|
|
2438
|
+
* @param address Signer address. Lowercased so checksum casing cannot fork the
|
|
2439
|
+
* identity into two distinct keys for the same account.
|
|
2440
|
+
*/
|
|
2441
|
+
declare function deriveSpendingKeyTypedData(chainId: number, address: string): SpendingKeyTypedData;
|
|
2442
|
+
/**
|
|
2443
|
+
* Message the user signs on signers without EIP-712 (Substrate: sr25519 via VRF,
|
|
2444
|
+
* ed25519 via signRaw).
|
|
2445
|
+
*
|
|
2446
|
+
* Substrate wallets render the raw string, so the warning leads the text — it is
|
|
2447
|
+
* the only channel a malicious extension cannot rewrite. Its protection is
|
|
2448
|
+
* therefore conditional on the wallet not truncating the message; when it does,
|
|
2449
|
+
* only the HKDF domain separation in `PrivacyKeys` still applies.
|
|
2450
|
+
*/
|
|
2451
|
+
declare function deriveSpendingKeyMessageV2(chainId: number, address: string): string;
|
|
2452
|
+
/**
|
|
2453
|
+
* @deprecated INSECURE — v1 derivation. Signs a fixed public string via
|
|
2454
|
+
* `personal_sign`, so any dapp can request it and, because the signature is
|
|
2455
|
+
* deterministic, reconstruct the user's spending key, viewing key and vault key.
|
|
2456
|
+
*
|
|
2457
|
+
* Retained ONLY so existing v1 notes can be swept into a v2 identity. Never call
|
|
2458
|
+
* it on a connect/login path. Use {@link deriveSpendingKeyTypedData} (EVM) or
|
|
2459
|
+
* {@link deriveSpendingKeyMessageV2} (Substrate) instead.
|
|
2460
|
+
*/
|
|
2461
|
+
declare function deriveSpendingKeyMessage(chainId: number, address: string): string;
|
|
2462
|
+
|
|
2321
2463
|
/**
|
|
2322
2464
|
* PrivacyKeys
|
|
2323
2465
|
*
|
|
2324
|
-
* Pure cryptographic derivation
|
|
2325
|
-
*
|
|
2466
|
+
* Pure cryptographic derivation for the Orbinum shielded pool identity: turns a
|
|
2467
|
+
* wallet signature into key material, and key material into the public values
|
|
2468
|
+
* that make up a privacy address. Protocol-level only — no storage, UI or
|
|
2469
|
+
* session concerns. What the user *signs* to produce that signature lives in
|
|
2470
|
+
* `SpendingKeyRequest`.
|
|
2471
|
+
*
|
|
2472
|
+
* Full derivation chain:
|
|
2326
2473
|
*
|
|
2327
|
-
*
|
|
2328
|
-
*
|
|
2329
|
-
*
|
|
2330
|
-
*
|
|
2331
|
-
*
|
|
2474
|
+
* signature ──HKDF(info="orbinum-sk-{version}:{chainId}:{address}")──► masterBytes (32B)
|
|
2475
|
+
* │
|
|
2476
|
+
* ┌─────────────────────────────────────────────────────────────────┤
|
|
2477
|
+
* ▼ ▼
|
|
2478
|
+
* spendingKey = BigInt(masterBytes) % BABYJUB_SUBORDER vaultKey (see vault/)
|
|
2479
|
+
* │ = HKDF(masterBytes, "orbinum-vault-key-v1")
|
|
2480
|
+
* ├──► ownerPk = BJJ_mul(Base8, spendingKey).Ax (public)
|
|
2481
|
+
* │
|
|
2482
|
+
* └──► ivsk = HKDF(LE32(spendingKey), info="orbinum-ivk-v1") (secret)
|
|
2483
|
+
* └──► ivk = packPoint(BJJ_mul(Base8, ivsk_scalar)) (public)
|
|
2332
2484
|
*
|
|
2333
|
-
*
|
|
2334
|
-
*
|
|
2335
|
-
*
|
|
2336
|
-
*
|
|
2485
|
+
* VERSIONING: the HKDF `info` carries the identity version, so v1 and v2 are
|
|
2486
|
+
* cryptographically disjoint even given identical signature bytes. This is the
|
|
2487
|
+
* layer that still separates the identities when the message-level defense fails
|
|
2488
|
+
* — see the security model in `SpendingKeyRequest`.
|
|
2337
2489
|
*
|
|
2338
|
-
*
|
|
2339
|
-
* Num2Bits(253)
|
|
2340
|
-
*
|
|
2490
|
+
* MODULUS: reduce mod BABYJUB_SUBORDER, never BN254_R. circomlib's BabyPbk uses
|
|
2491
|
+
* Num2Bits(253), asserting spending_key < 2^253. BABYJUB_SUBORDER < 2^252
|
|
2492
|
+
* satisfies it; BN254_R ≈ 2^254.8 does not — ~34% of values would fail at runtime.
|
|
2341
2493
|
*/
|
|
2494
|
+
/** Identity version. `v1` is the legacy personal_sign scheme, kept for sweeping only. */
|
|
2495
|
+
type KeyVersion = 'v1' | 'v2';
|
|
2342
2496
|
/**
|
|
2343
|
-
*
|
|
2344
|
-
*
|
|
2497
|
+
* Shortest signature any supported signer produces: sr25519 VRF output is 32
|
|
2498
|
+
* bytes, ed25519 is 64, ECDSA `personal_sign` is 65. Anything shorter is not a
|
|
2499
|
+
* signature, so it must never reach the KDF.
|
|
2345
2500
|
*/
|
|
2346
|
-
declare
|
|
2501
|
+
declare const MIN_SIGNATURE_BYTES = 32;
|
|
2347
2502
|
/**
|
|
2348
2503
|
* Derives the 32-byte master key bytes from a wallet signature.
|
|
2349
2504
|
*
|
|
2350
|
-
* masterBytes = HKDF-SHA256(ikm=sigBytes, salt=empty, info="orbinum-sk-
|
|
2505
|
+
* masterBytes = HKDF-SHA256(ikm=sigBytes, salt=empty, info="orbinum-sk-{version}:{chainId}:{address}")
|
|
2351
2506
|
*
|
|
2352
2507
|
* These bytes are the stable root for ALL derived keys:
|
|
2353
2508
|
* - spendingKey (circuit scalar) = BigInt(masterBytes) % BABYJUB_SUBORDER
|
|
@@ -2357,12 +2512,19 @@ declare function deriveSpendingKeyMessage(chainId: number, address: string): str
|
|
|
2357
2512
|
* Separating masterBytes from the circuit scalar means the viewingSecretKey and
|
|
2358
2513
|
* vault key are STABLE across any future change to the modulus — they never
|
|
2359
2514
|
* depend on which prime field the circuit uses.
|
|
2515
|
+
*
|
|
2516
|
+
* The version is folded into the HKDF `info`, so v1 and v2 stay disjoint even if
|
|
2517
|
+
* the underlying signature bytes were somehow identical.
|
|
2518
|
+
*
|
|
2519
|
+
* @param version Defaults to 'v2'. Pass 'v1' only from the legacy sweep flow.
|
|
2520
|
+
* @throws If the signature is not valid hex, or carries less entropy than the
|
|
2521
|
+
* shortest real signing scheme (see {@link MIN_SIGNATURE_BYTES}).
|
|
2360
2522
|
*/
|
|
2361
|
-
declare function deriveMasterKeyBytes(signatureHex: string, chainId: number, address: string): Promise<Uint8Array>;
|
|
2523
|
+
declare function deriveMasterKeyBytes(signatureHex: string, chainId: number, address: string, version?: KeyVersion): Promise<Uint8Array>;
|
|
2362
2524
|
/**
|
|
2363
2525
|
* Derives an Orbinum spending key from a wallet signature.
|
|
2364
2526
|
*
|
|
2365
|
-
* Uses HKDF-SHA256(ikm=sigBytes, salt=empty, info="orbinum-sk-
|
|
2527
|
+
* Uses HKDF-SHA256(ikm=sigBytes, salt=empty, info="orbinum-sk-{version}:{chainId}:{address}")
|
|
2366
2528
|
* and reduces the resulting 32-byte value modulo BABYJUB_SUBORDER.
|
|
2367
2529
|
*
|
|
2368
2530
|
* IMPORTANT: viewingSecretKey and vaultKey must be derived from masterBytes (via
|
|
@@ -2372,9 +2534,12 @@ declare function deriveMasterKeyBytes(signatureHex: string, chainId: number, add
|
|
|
2372
2534
|
* @param signatureHex 0x-prefixed or bare hex of the wallet signature.
|
|
2373
2535
|
* @param chainId Chain ID used when building the signing message.
|
|
2374
2536
|
* @param address Signer address (EVM or SS58) used in the signing message.
|
|
2537
|
+
* @param version Defaults to 'v2'. Pass 'v1' only from the legacy sweep flow.
|
|
2375
2538
|
* @returns bigint in [1, BABYJUB_SUBORDER)
|
|
2539
|
+
* @throws If the signature is not valid hex or is shorter than
|
|
2540
|
+
* {@link MIN_SIGNATURE_BYTES} — see `deriveMasterKeyBytes`.
|
|
2376
2541
|
*/
|
|
2377
|
-
declare function deriveSpendingKeyFromSignature(signatureHex: string, chainId: number, address: string): Promise<bigint>;
|
|
2542
|
+
declare function deriveSpendingKeyFromSignature(signatureHex: string, chainId: number, address: string, version?: KeyVersion): Promise<bigint>;
|
|
2378
2543
|
/**
|
|
2379
2544
|
* Derive a 32-byte viewing secret key (ivsk) from the spending key.
|
|
2380
2545
|
* ivsk = HKDF-SHA256(ikm=bigintTo32Le(spendingKey), info="orbinum-ivk-v1")
|
|
@@ -4269,4 +4434,4 @@ interface ExtrinsicFailedData {
|
|
|
4269
4434
|
dispatch_info: DispatchInfo;
|
|
4270
4435
|
}
|
|
4271
4436
|
|
|
4272
|
-
export { type AccountListing, type AccountMappedData, type AccountMappedEvent, type AccountMappingCall, type AccountMappingEvent, AccountMappingModule, AccountMappingPrecompile, type AccountMetadata, type AccountUnmappedEvent, type ActiveVersionSetEvent, type AddChainLinkArgs, type AddChainLinkParams, type AddSupportedChainArgs, type AliasFullIdentity, type AliasInfo, type AliasListedForSaleEvent, type AliasOnSaleData, type AliasRegisteredData, type AliasRegisteredEvent, type AliasReleasedEvent, type AliasSaleCancelledEvent, type AliasSoldData, type AliasSoldEvent, type AliasTransferredData, type AliasTransferredEvent, type AssetRegisteredEvent, type AssetUnverifiedEvent, type AssetVerifiedEvent, BABYJUB_SUBORDER, BN254_R, type BatchRegisterVerificationKeysArgs, type BatchVerificationKeysRegisteredEvent, type BlockInfo, type BuyAliasArgs, type Bytes32, CURRENT_CIRCUIT_VERSION, type ChainInfo, type ChainLink, type ChainLinkAddedEvent, type ChainLinkRemovedEvent, CircuitId, CircuitId as CircuitIdType, CircuitVersionResolver, type ClaimShieldedFeesParams, type ClientProviderConfig, type CommitmentsInsertedEvent, type CommitmentsInsertedEventData, type ConnectionStatus, CryptoPrecompiles, type DecodedAddChainLinkArgs, type DecodedBatchArgs, type DecodedDispatchAsPrivateLinkArgs, type DecodedEthereumTransactArgs, type DecodedEvmCallArgs, type DecodedPrecompile, type DecodedPrivateTransferArgs, type DecodedPutAliasForSaleArgs, type DecodedRegisterAliasArgs, type DecodedRemarkArgs, type DecodedRevealPrivateLinkArgs, type DecodedSetAccountMetadataArgs, type DecodedShieldArgs, type DecodedShieldBatchArgs, type DecodedShieldBatchOperation, type DecodedSudoArgs, type DecodedTransferAllArgs, type DecodedTransferArgs, type DecodedTransferKeepAliveArgs, type DecodedUnshieldArgs, type DecryptedMemo, type DispatchAsLinkedAccountArgs, type DispatchAsLinkedParams, type DispatchAsPrivateLinkArgs, type DispatchError, type DispatchInfo, type DynamicBuilder, ENCRYPTED_MEMO_SIZE, EncryptedMemo, type EncryptedNoteRecord, type EndowedEventData, type EthereumExecutedData, type EventData, type EventPhase, type EventRecord, type EvmAddressInfo, type EvmBlock, EvmClient, type EvmExecutedData, type EvmExitReason, EvmExplorer, type EvmLog, type EvmSigner, type EvmTransaction, type EvmTxRequest, type EvmTxSummary, type ExtrinsicDecoder, type ExtrinsicFailedData, type ExtrinsicSuccessData, type FeeClaimProofInputs, type FormatOptions, KNOWN_PRECOMPILES, type KnownPrecompileInfo, type ListingInfo, type MerkleRootUpdatedData, type MerkleRootUpdatedEvent, type MerkleTreeInfo, type MetadataUpdatedEvent, NoteBuilder, type NoteDisclosure, type NoteInput, type NoteStatusUpdate, type NullifiersSpentEvent, type NullifiersSpentEventData, OrbinumClient, type OrbinumClientConfig, OrbinumClientProvider, PRECOMPILE_ADDR, PrivacyKeyManager, type PrivacyMerkleProof, PrivacyModule, type PrivateChainLinkAddedEvent, type PrivateChainLinkRemovedEvent, type PrivateChainLinkRevealedEvent, type PrivateLink, type PrivateLinkDispatchExecutedEvent, type PrivateTransferArgs, type PrivateTransferInput, type PrivateTransferOutput, type PrivateTransferParams, type PrivateTransferProofInputs, type ProofVerificationFailedEvent, type ProofVerifiedEvent, type ProxyCallExecutedEvent, type PutAliasOnSaleArgs, type PutOnSaleParams, type RawBlock, type RawBlockHeader, type RawTransferInput, type RawTransferOutput, type RegisterAliasArgs, type RegisterAssetArgs, type RegisterPrivateLinkArgs, type RegisterVerificationKeyArgs, type RelayerInfo, RelayerStatusModule, type RemoveChainLinkArgs, type RemovePrivateLinkArgs, type RemoveSupportedChainArgs, type RemoveVerificationKeyArgs, type ReservedEventData, type ResolvedAlias, type ResolvedSpendVersion, type RevealPrivateLinkArgs, type RpcV2MerkleProof, type RpcV2NullifierStatus, type RpcV2PoolAssetBalance, type RpcV2PoolStats, SLIP0044_NAMESPACE, type ScanCommitment, type SelfEphWindowEntry, type SetAccountMetadataArgs, type SetActiveVersionArgs, type SetMetadataParams, type ShieldArgs, type ShieldBatchArgs, type ShieldBatchItem, type ShieldBatchParams, type ShieldOperation, type ShieldParams, type ShieldedEvent, type ShieldedEventData, type ShieldedPoolCall, type ShieldedPoolEvent, ShieldedPoolModule, ShieldedPoolPrecompile, SignatureScheme, type StatusChangeEvent, type StatusListener, SubstrateClient, type SupportedChain, type SupportedChainAddedEvent, type SupportedChainRemovedEvent, type SystemHealth, type TokenInfo, type TokenTransfer, type TransferAliasArgs, type TransferEventData, type TransferInputNote, type TransferOutputNote, type TryDecryptOptions, type TxResult, type UnsafeTxOptions, type UnshieldArgs, type UnshieldParams, type UnshieldProofInputs, type UnshieldedEvent, type UnshieldedEventData, type UnverifyAssetArgs, VaultLockedError, type VerificationKeyRegisteredEvent, type VerificationKeyRemovedEvent, type VerifyAssetArgs, type VerifyProofArgs, type VkEntry, type ZkNote, type ZkVerifierCall, type ZkVerifierCircuitVersionInfo, type ZkVerifierEvent, type ZkVerifierHistoricalVersion, ZkVerifierModule, type ZkVerifierVersionStats, type ZkVerifierVkHash, accountIdHexToSs58, addressToAccountIdHex, applyNoteStatus, bigintTo32Be, bigintTo32Le, bigintTo32LeArr, blindTag, buildDummyTransferInput, bytesToBigintLE, computeNoteCommitment, computeNullifier, computePathIndices, createNoteDisclosureKey, decodeNoteDisclosureKey, decodePrecompileCalldata, decryptJson, decryptNoteRecord, deriveMasterKeyBytes, deriveOwnerPk, deriveSelfEphSk, deriveSpendingKeyFromSignature, deriveSpendingKeyMessage, deriveStealthOwnerPk, deriveStealthSk, deriveVaultBlindKey, deriveVaultKey, deriveViewTag, deriveViewingPublicKey, deriveViewingSecretKey, encryptJson, encryptNote, ensureHexPrefix, evmAddressToAccountId, evmToImplicitSubstrate, evmToMappedAccountHex, evmToSubstrate, formatBalance, formatORB, fromBase64, fromHex, generateFeeClaimProof, generateTransferProof, generateUnshieldProof, getPrecompileLabel, hexToBigint, hexToNumber, implicitSubstrateToEvm, isEvmAddress, isImplicitEvmAccount, isSs58, isSubstrateAddress, isUnifiedAddress, leHexToBigint, mapExtrinsicArgs, mapZkEventData, normalizeEvmAddress, noteBlindTag, randomBlinding, recoverOwnerPkPoint, selectNotes, selfEphWindow, shortHash, substrateSs58ToAccountIdHex, substrateToEvm, toBase64, toHex, toTxResult, truncateMiddle, tryDecryptNote, tryDecryptNoteVerbose, vaultReplacer, vaultReviver };
|
|
4437
|
+
export { type AccountListing, type AccountMappedData, type AccountMappedEvent, type AccountMappingCall, type AccountMappingEvent, AccountMappingModule, AccountMappingPrecompile, type AccountMetadata, type AccountUnmappedEvent, type ActiveVersionSetEvent, type AddChainLinkArgs, type AddChainLinkParams, type AddSupportedChainArgs, type AliasFullIdentity, type AliasInfo, type AliasListedForSaleEvent, type AliasOnSaleData, type AliasRegisteredData, type AliasRegisteredEvent, type AliasReleasedEvent, type AliasSaleCancelledEvent, type AliasSoldData, type AliasSoldEvent, type AliasTransferredData, type AliasTransferredEvent, type AssetRegisteredEvent, type AssetUnverifiedEvent, type AssetVerifiedEvent, BABYJUB_SUBORDER, BN254_R, type BatchRegisterVerificationKeysArgs, type BatchVerificationKeysRegisteredEvent, type BlockInfo, type BuyAliasArgs, type Bytes32, CURRENT_CIRCUIT_VERSION, type ChainInfo, type ChainLink, type ChainLinkAddedEvent, type ChainLinkRemovedEvent, CircuitId, CircuitId as CircuitIdType, CircuitVersionResolver, type ClaimShieldedFeesParams, type ClientProviderConfig, type CommitmentsInsertedEvent, type CommitmentsInsertedEventData, type ConnectionStatus, CryptoPrecompiles, type DecodedAddChainLinkArgs, type DecodedBatchArgs, type DecodedDispatchAsPrivateLinkArgs, type DecodedEthereumTransactArgs, type DecodedEvmCallArgs, type DecodedPrecompile, type DecodedPrivateTransferArgs, type DecodedPutAliasForSaleArgs, type DecodedRegisterAliasArgs, type DecodedRemarkArgs, type DecodedRevealPrivateLinkArgs, type DecodedSetAccountMetadataArgs, type DecodedShieldArgs, type DecodedShieldBatchArgs, type DecodedShieldBatchOperation, type DecodedSudoArgs, type DecodedTransferAllArgs, type DecodedTransferArgs, type DecodedTransferKeepAliveArgs, type DecodedUnshieldArgs, type DecryptedMemo, type DispatchAsLinkedAccountArgs, type DispatchAsLinkedParams, type DispatchAsPrivateLinkArgs, type DispatchError, type DispatchInfo, type DynamicBuilder, ENCRYPTED_MEMO_SIZE, EncryptedMemo, type EncryptedNoteRecord, type EndowedEventData, type EthereumExecutedData, type EventData, type EventPhase, type EventRecord, type EvmAddressInfo, type EvmBlock, EvmClient, type EvmExecutedData, type EvmExitReason, EvmExplorer, type EvmLog, type EvmSigner, type EvmTransaction, type EvmTxRequest, type EvmTxSummary, type ExtrinsicDecoder, type ExtrinsicFailedData, type ExtrinsicSuccessData, type FeeClaimProofInputs, type FormatOptions, KNOWN_PRECOMPILES, type KeyVersion, type KnownPrecompileInfo, type ListingInfo, MIN_SIGNATURE_BYTES, type MerkleRootUpdatedData, type MerkleRootUpdatedEvent, type MerkleTreeInfo, type MetadataUpdatedEvent, NoteBuilder, type NoteDisclosure, type NoteInput, type NoteStatusUpdate, type NullifiersSpentEvent, type NullifiersSpentEventData, OrbinumClient, type OrbinumClientConfig, OrbinumClientProvider, PRECOMPILE_ADDR, PrivacyKeyManager, type PrivacyMerkleProof, PrivacyModule, type PrivateChainLinkAddedEvent, type PrivateChainLinkRemovedEvent, type PrivateChainLinkRevealedEvent, type PrivateLink, type PrivateLinkDispatchExecutedEvent, type PrivateTransferArgs, type PrivateTransferInput, type PrivateTransferOutput, type PrivateTransferParams, type PrivateTransferProofInputs, type ProofVerificationFailedEvent, type ProofVerifiedEvent, type ProxyCallExecutedEvent, type PutAliasOnSaleArgs, type PutOnSaleParams, type RawBlock, type RawBlockHeader, type RawTransferInput, type RawTransferOutput, type RegisterAliasArgs, type RegisterAssetArgs, type RegisterPrivateLinkArgs, type RegisterVerificationKeyArgs, type RelayerInfo, RelayerStatusModule, type RemoveChainLinkArgs, type RemovePrivateLinkArgs, type RemoveSupportedChainArgs, type RemoveVerificationKeyArgs, type ReservedEventData, type ResolvedAlias, type ResolvedSpendVersion, type RevealPrivateLinkArgs, type RpcV2MerkleProof, type RpcV2NullifierStatus, type RpcV2PoolAssetBalance, type RpcV2PoolStats, SLIP0044_NAMESPACE, SPENDING_KEY_VERIFYING_CONTRACT, SPENDING_KEY_WARNING, type ScanCommitment, type SelfEphWindowEntry, type SetAccountMetadataArgs, type SetActiveVersionArgs, type SetMetadataParams, type ShieldArgs, type ShieldBatchArgs, type ShieldBatchItem, type ShieldBatchParams, type ShieldOperation, type ShieldParams, type ShieldedEvent, type ShieldedEventData, type ShieldedPoolCall, type ShieldedPoolEvent, ShieldedPoolModule, ShieldedPoolPrecompile, SignatureScheme, type SpendingKeyTypedData, type StatusChangeEvent, type StatusListener, SubstrateClient, type SupportedChain, type SupportedChainAddedEvent, type SupportedChainRemovedEvent, type SystemHealth, type TokenInfo, type TokenTransfer, type TransferAliasArgs, type TransferEventData, type TransferInputNote, type TransferOutputNote, type TryDecryptOptions, type TxResult, type UnsafeTxOptions, type UnshieldArgs, type UnshieldParams, type UnshieldProofInputs, type UnshieldedEvent, type UnshieldedEventData, type UnverifyAssetArgs, VaultLockedError, type VerificationKeyRegisteredEvent, type VerificationKeyRemovedEvent, type VerifyAssetArgs, type VerifyProofArgs, type VkEntry, type ZkNote, type ZkVerifierCall, type ZkVerifierCircuitVersionInfo, type ZkVerifierEvent, type ZkVerifierHistoricalVersion, ZkVerifierModule, type ZkVerifierVersionStats, type ZkVerifierVkHash, accountIdHexToSs58, addressToAccountIdHex, applyNoteStatus, bigintTo32Be, bigintTo32Le, bigintTo32LeArr, blindTag, buildDummyTransferInput, bytesToBigintLE, computeNoteCommitment, computeNullifier, computePathIndices, createNoteDisclosureKey, decodeNoteDisclosureKey, decodePrecompileCalldata, decryptJson, decryptNoteRecord, deriveMasterKeyBytes, deriveOwnerPk, deriveSelfEphSk, deriveSpendingKeyFromSignature, deriveSpendingKeyMessage, deriveSpendingKeyMessageV2, deriveSpendingKeyTypedData, deriveStealthOwnerPk, deriveStealthSk, deriveVaultBlindKey, deriveVaultKey, deriveViewTag, deriveViewingPublicKey, deriveViewingSecretKey, encryptJson, encryptNote, ensureHexPrefix, evmAddressToAccountId, evmToImplicitSubstrate, evmToMappedAccountHex, evmToSubstrate, formatBalance, formatORB, fromBase64, fromHex, generateFeeClaimProof, generateTransferProof, generateUnshieldProof, getPrecompileLabel, hexToBigint, hexToNumber, implicitSubstrateToEvm, isEvmAddress, isImplicitEvmAccount, isSs58, isSubstrateAddress, isUnifiedAddress, leHexToBigint, mapExtrinsicArgs, mapZkEventData, normalizeEvmAddress, noteBlindTag, randomBlinding, recoverOwnerPkPoint, selectNotes, selfEphWindow, shortHash, substrateSs58ToAccountIdHex, substrateToEvm, toBase64, toHex, toTxResult, truncateMiddle, tryDecryptNote, tryDecryptNoteVerbose, vaultReplacer, vaultReviver };
|
package/dist/index.js
CHANGED
|
@@ -37,6 +37,7 @@ __export(index_exports, {
|
|
|
37
37
|
EvmExplorer: () => EvmExplorer,
|
|
38
38
|
KNOWN_PRECOMPILES: () => KNOWN_PRECOMPILES,
|
|
39
39
|
Keccak256: () => import_substrate_bindings3.Keccak256,
|
|
40
|
+
MIN_SIGNATURE_BYTES: () => MIN_SIGNATURE_BYTES,
|
|
40
41
|
NoteBuilder: () => NoteBuilder,
|
|
41
42
|
OrbinumClient: () => OrbinumClient,
|
|
42
43
|
OrbinumClientProvider: () => OrbinumClientProvider,
|
|
@@ -45,6 +46,8 @@ __export(index_exports, {
|
|
|
45
46
|
PrivacyModule: () => PrivacyModule,
|
|
46
47
|
RelayerStatusModule: () => RelayerStatusModule,
|
|
47
48
|
SLIP0044_NAMESPACE: () => SLIP0044_NAMESPACE,
|
|
49
|
+
SPENDING_KEY_VERIFYING_CONTRACT: () => SPENDING_KEY_VERIFYING_CONTRACT,
|
|
50
|
+
SPENDING_KEY_WARNING: () => SPENDING_KEY_WARNING,
|
|
48
51
|
ShieldedPoolModule: () => ShieldedPoolModule,
|
|
49
52
|
ShieldedPoolPrecompile: () => ShieldedPoolPrecompile,
|
|
50
53
|
SignatureScheme: () => SignatureScheme,
|
|
@@ -77,6 +80,8 @@ __export(index_exports, {
|
|
|
77
80
|
deriveSelfEphSk: () => deriveSelfEphSk,
|
|
78
81
|
deriveSpendingKeyFromSignature: () => deriveSpendingKeyFromSignature,
|
|
79
82
|
deriveSpendingKeyMessage: () => deriveSpendingKeyMessage,
|
|
83
|
+
deriveSpendingKeyMessageV2: () => deriveSpendingKeyMessageV2,
|
|
84
|
+
deriveSpendingKeyTypedData: () => deriveSpendingKeyTypedData,
|
|
80
85
|
deriveStealthOwnerPk: () => deriveStealthOwnerPk,
|
|
81
86
|
deriveStealthSk: () => deriveStealthSk,
|
|
82
87
|
deriveVaultBlindKey: () => deriveVaultBlindKey,
|
|
@@ -225,6 +230,27 @@ var SubstrateClient = class _SubstrateClient {
|
|
|
225
230
|
_httpUrl;
|
|
226
231
|
_dynamicBuilder = null;
|
|
227
232
|
_extDecoder = null;
|
|
233
|
+
_inflightTxCount = 0;
|
|
234
|
+
/**
|
|
235
|
+
* `true` while any submitted transaction is still waiting for finalization.
|
|
236
|
+
* Connection managers use this to defer destroying the client — killing the
|
|
237
|
+
* WS mid-submit rejects the pending tx with "Client destroyed" even though
|
|
238
|
+
* it may still land on-chain.
|
|
239
|
+
*
|
|
240
|
+
* Only covers promise-based submits (`submit`, `submitUnsignedAndWatch`,
|
|
241
|
+
* `signAndSubmit`); observable-based `submitAndWatch` callers are not tracked.
|
|
242
|
+
*/
|
|
243
|
+
get hasInflightTx() {
|
|
244
|
+
return this._inflightTxCount > 0;
|
|
245
|
+
}
|
|
246
|
+
async trackTx(p) {
|
|
247
|
+
this._inflightTxCount++;
|
|
248
|
+
try {
|
|
249
|
+
return await p;
|
|
250
|
+
} finally {
|
|
251
|
+
this._inflightTxCount--;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
228
254
|
/**
|
|
229
255
|
* Connects to the Orbinum node via WebSocket.
|
|
230
256
|
* Throws if the node does not respond within `timeoutMs`.
|
|
@@ -437,7 +463,7 @@ var SubstrateClient = class _SubstrateClient {
|
|
|
437
463
|
* Submits a pre-signed extrinsic (hex string) and waits for finalization.
|
|
438
464
|
*/
|
|
439
465
|
async submit(signedHex) {
|
|
440
|
-
return this._papi.submit(import_polkadot_api.Binary.fromHex(signedHex));
|
|
466
|
+
return this.trackTx(this._papi.submit(import_polkadot_api.Binary.fromHex(signedHex)));
|
|
441
467
|
}
|
|
442
468
|
/**
|
|
443
469
|
* Submits a pre-signed extrinsic and returns an Observable of tx lifecycle events.
|
|
@@ -452,14 +478,14 @@ var SubstrateClient = class _SubstrateClient {
|
|
|
452
478
|
* The bare tx bytes are produced by `tx.getBareTx()` from polkadot-api.
|
|
453
479
|
*/
|
|
454
480
|
async submitUnsignedAndWatch(bareTx) {
|
|
455
|
-
return this._papi.submit(bareTx);
|
|
481
|
+
return this.trackTx(this._papi.submit(bareTx));
|
|
456
482
|
}
|
|
457
483
|
/**
|
|
458
484
|
* Convenience: wrap raw call bytes and sign+submit in one step.
|
|
459
485
|
*/
|
|
460
486
|
async signAndSubmit(callData, signer) {
|
|
461
487
|
const tx = await this.txFromCallData(callData);
|
|
462
|
-
return tx.signAndSubmit(signer);
|
|
488
|
+
return this.trackTx(tx.signAndSubmit(signer));
|
|
463
489
|
}
|
|
464
490
|
/** Closes the WebSocket connection. */
|
|
465
491
|
destroy() {
|
|
@@ -716,17 +742,46 @@ var EvmClient = class {
|
|
|
716
742
|
}
|
|
717
743
|
return json.result ?? null;
|
|
718
744
|
}
|
|
745
|
+
/**
|
|
746
|
+
* Fetches a transaction by hash, or `null` when the node no longer knows it
|
|
747
|
+
* (never mined and evicted from the pool). Unlike `request`, a `null`
|
|
748
|
+
* result is a valid answer here, not an error.
|
|
749
|
+
*/
|
|
750
|
+
async getTransactionByHash(txHash) {
|
|
751
|
+
const res = await postJsonWithRetry(
|
|
752
|
+
this.rpcUrl,
|
|
753
|
+
JSON.stringify({
|
|
754
|
+
id: 1,
|
|
755
|
+
jsonrpc: "2.0",
|
|
756
|
+
method: "eth_getTransactionByHash",
|
|
757
|
+
params: [txHash]
|
|
758
|
+
})
|
|
759
|
+
);
|
|
760
|
+
if (!res.ok) throw new Error(`EVM HTTP ${res.status}: ${res.statusText}`);
|
|
761
|
+
const json = await res.json();
|
|
762
|
+
if (json.error) {
|
|
763
|
+
throw new Error(`EVM RPC [${json.error.code}]: ${json.error.message}`);
|
|
764
|
+
}
|
|
765
|
+
return json.result ?? null;
|
|
766
|
+
}
|
|
719
767
|
/**
|
|
720
768
|
* Polls `eth_getTransactionReceipt` until the transaction is included in a block.
|
|
721
769
|
*
|
|
770
|
+
* After `timeoutMs`, the tx-pool is consulted: a tx no longer known to the
|
|
771
|
+
* node is reported as dropped (safe to retry), while a tx still in the pool
|
|
772
|
+
* gets an extended grace window (up to 4× `timeoutMs`) before a "still
|
|
773
|
+
* pending" error — it may confirm later, so callers must NOT blindly retry.
|
|
774
|
+
*
|
|
722
775
|
* @param txHash - The transaction hash to wait for.
|
|
723
776
|
* @param intervalMs - Polling interval in milliseconds (default: 500).
|
|
724
777
|
* @param timeoutMs - Maximum time to wait in milliseconds (default: 60_000).
|
|
725
778
|
* @returns The transaction receipt once mined.
|
|
726
|
-
* @throws If the transaction is
|
|
779
|
+
* @throws If the transaction dropped, is still pending after the grace window, or reverted (`status == 0x0`).
|
|
727
780
|
*/
|
|
728
781
|
async waitForReceipt(txHash, intervalMs = 500, timeoutMs = 6e4) {
|
|
729
|
-
const
|
|
782
|
+
const start = Date.now();
|
|
783
|
+
const hardDeadline = start + timeoutMs * 4;
|
|
784
|
+
let deadline = start + timeoutMs;
|
|
730
785
|
while (Date.now() < deadline) {
|
|
731
786
|
const receipt = await this.getTransactionReceipt(txHash);
|
|
732
787
|
if (receipt !== null) {
|
|
@@ -737,10 +792,7 @@ var EvmClient = class {
|
|
|
737
792
|
if (!revertDetail) {
|
|
738
793
|
try {
|
|
739
794
|
const blockParam = receipt["blockNumber"] ?? "latest";
|
|
740
|
-
const rawTx = await this.
|
|
741
|
-
"eth_getTransactionByHash",
|
|
742
|
-
[txHash]
|
|
743
|
-
).catch(() => null);
|
|
795
|
+
const rawTx = await this.getTransactionByHash(txHash).catch(() => null);
|
|
744
796
|
if (rawTx) {
|
|
745
797
|
const calldata = rawTx["input"] ?? rawTx["data"];
|
|
746
798
|
if (calldata) {
|
|
@@ -763,8 +815,19 @@ var EvmClient = class {
|
|
|
763
815
|
return receipt;
|
|
764
816
|
}
|
|
765
817
|
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
818
|
+
if (Date.now() >= deadline && Date.now() < hardDeadline) {
|
|
819
|
+
const known = await this.getTransactionByHash(txHash).catch(() => void 0);
|
|
820
|
+
if (known === null) {
|
|
821
|
+
throw new Error(
|
|
822
|
+
`Transaction dropped from the tx pool (not mined within ${Date.now() - start}ms): ${txHash}`
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
deadline = Math.min(deadline + timeoutMs, hardDeadline);
|
|
826
|
+
}
|
|
766
827
|
}
|
|
767
|
-
throw new Error(
|
|
828
|
+
throw new Error(
|
|
829
|
+
`Transaction still pending after ${Date.now() - start}ms: ${txHash} \u2014 it may still confirm; check the hash on the explorer before retrying`
|
|
830
|
+
);
|
|
768
831
|
}
|
|
769
832
|
};
|
|
770
833
|
|
|
@@ -3368,7 +3431,7 @@ var DEFAULT_HEARTBEAT_TIMEOUT_MS = 4e3;
|
|
|
3368
3431
|
var DEFAULT_RECONNECT_BASE_MS = 3e3;
|
|
3369
3432
|
var DEFAULT_RECONNECT_MAX_MS = 3e4;
|
|
3370
3433
|
var DEFAULT_STABLE_AFTER_MS = 1e4;
|
|
3371
|
-
var OrbinumClientProvider = class {
|
|
3434
|
+
var OrbinumClientProvider = class _OrbinumClientProvider {
|
|
3372
3435
|
config;
|
|
3373
3436
|
connectTimeoutMs;
|
|
3374
3437
|
heartbeatIntervalMs;
|
|
@@ -3385,6 +3448,7 @@ var OrbinumClientProvider = class {
|
|
|
3385
3448
|
_reconnectTimer = null;
|
|
3386
3449
|
_stableTimer = null;
|
|
3387
3450
|
_reconnectAttempt = 0;
|
|
3451
|
+
_probeFailures = 0;
|
|
3388
3452
|
// ─── Events ─────────────────────────────────────────────────────────────
|
|
3389
3453
|
_listeners = /* @__PURE__ */ new Set();
|
|
3390
3454
|
/** Creates a new provider with the given configuration. Does not connect automatically — call `connect()` to initiate. */
|
|
@@ -3517,13 +3581,36 @@ var OrbinumClientProvider = class {
|
|
|
3517
3581
|
}
|
|
3518
3582
|
}
|
|
3519
3583
|
// ─── Heartbeat ──────────────────────────────────────────────────────────
|
|
3584
|
+
/**
|
|
3585
|
+
* Consecutive failed probes required before the client is torn down. A
|
|
3586
|
+
* single missed probe is routine (throttled background tab, node busy
|
|
3587
|
+
* verifying a ZK proof, transient network blip) — destroying the client on
|
|
3588
|
+
* it rejects every in-flight request with "Client destroyed" even though
|
|
3589
|
+
* the tx may still land on-chain.
|
|
3590
|
+
*/
|
|
3591
|
+
static PROBE_FAILURE_THRESHOLD = 2;
|
|
3592
|
+
/**
|
|
3593
|
+
* With a tx awaiting finalization, tolerate more missed probes: an unsigned
|
|
3594
|
+
* private_transfer/unshield makes the node CPU-bound on proof verification,
|
|
3595
|
+
* which is exactly when probes time out — tearing down then kills the very
|
|
3596
|
+
* tx being processed.
|
|
3597
|
+
*/
|
|
3598
|
+
static PROBE_FAILURE_THRESHOLD_INFLIGHT = 6;
|
|
3520
3599
|
/** Starts the periodic heartbeat loop. Replaces any existing timer. */
|
|
3521
3600
|
startHeartbeat() {
|
|
3522
3601
|
this.stopHeartbeat();
|
|
3602
|
+
this._probeFailures = 0;
|
|
3523
3603
|
this._heartbeatTimer = setInterval(async () => {
|
|
3524
3604
|
if (this._status !== "connected" || !this._orbinumClient) return;
|
|
3525
3605
|
const alive = await this.probe();
|
|
3526
|
-
if (
|
|
3606
|
+
if (this._status !== "connected") return;
|
|
3607
|
+
if (alive) {
|
|
3608
|
+
this._probeFailures = 0;
|
|
3609
|
+
return;
|
|
3610
|
+
}
|
|
3611
|
+
this._probeFailures++;
|
|
3612
|
+
const threshold = this._orbinumClient?.substrate.hasInflightTx ? _OrbinumClientProvider.PROBE_FAILURE_THRESHOLD_INFLIGHT : _OrbinumClientProvider.PROBE_FAILURE_THRESHOLD;
|
|
3613
|
+
if (this._probeFailures >= threshold) {
|
|
3527
3614
|
this.setStatus("disconnected", "Node is unreachable");
|
|
3528
3615
|
this.teardownClient();
|
|
3529
3616
|
this.scheduleReconnect();
|
|
@@ -4119,23 +4206,66 @@ function randomBlinding() {
|
|
|
4119
4206
|
return n === 0n ? 1n : n % BN254_R;
|
|
4120
4207
|
}
|
|
4121
4208
|
|
|
4209
|
+
// src/privacy-keys/SpendingKeyRequest.ts
|
|
4210
|
+
var SPENDING_KEY_VERIFYING_CONTRACT = PRECOMPILE_ADDR.SHIELDED_POOL;
|
|
4211
|
+
var SPENDING_KEY_WARNING = "Signing this grants full control of your Orbinum private funds. Only sign on the official Orbinum app.";
|
|
4212
|
+
function deriveSpendingKeyTypedData(chainId, address) {
|
|
4213
|
+
return {
|
|
4214
|
+
domain: {
|
|
4215
|
+
name: "Orbinum Shielded Pool",
|
|
4216
|
+
version: "2",
|
|
4217
|
+
chainId,
|
|
4218
|
+
verifyingContract: SPENDING_KEY_VERIFYING_CONTRACT
|
|
4219
|
+
},
|
|
4220
|
+
types: {
|
|
4221
|
+
SpendingKeyDerivation: [
|
|
4222
|
+
{ name: "warning", type: "string" },
|
|
4223
|
+
{ name: "account", type: "address" }
|
|
4224
|
+
]
|
|
4225
|
+
},
|
|
4226
|
+
primaryType: "SpendingKeyDerivation",
|
|
4227
|
+
message: {
|
|
4228
|
+
warning: SPENDING_KEY_WARNING,
|
|
4229
|
+
account: address.toLowerCase()
|
|
4230
|
+
}
|
|
4231
|
+
};
|
|
4232
|
+
}
|
|
4233
|
+
function deriveSpendingKeyMessageV2(chainId, address) {
|
|
4234
|
+
return `\u26A0 ${SPENDING_KEY_WARNING}
|
|
4235
|
+
|
|
4236
|
+
orbinum-spending-key-v2
|
|
4237
|
+
${chainId}
|
|
4238
|
+
${address.toLowerCase()}`;
|
|
4239
|
+
}
|
|
4240
|
+
function deriveSpendingKeyMessage(chainId, address) {
|
|
4241
|
+
return `orbinum-spending-key-v1
|
|
4242
|
+
${chainId}
|
|
4243
|
+
${address.toLowerCase()}`;
|
|
4244
|
+
}
|
|
4245
|
+
|
|
4122
4246
|
// src/privacy-keys/PrivacyKeys.ts
|
|
4123
4247
|
var import_hkdf2 = require("@noble/hashes/hkdf.js");
|
|
4124
4248
|
var import_sha24 = require("@noble/hashes/sha2.js");
|
|
4125
4249
|
var import_baby_jubjub6 = require("@zk-kit/baby-jubjub");
|
|
4126
4250
|
var IVK_DOMAIN = new TextEncoder().encode("orbinum-ivk-v1");
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4251
|
+
var MIN_SIGNATURE_BYTES = 32;
|
|
4252
|
+
function assertUsableSignature(sigBytes) {
|
|
4253
|
+
if (sigBytes.length < MIN_SIGNATURE_BYTES) {
|
|
4254
|
+
throw new Error(
|
|
4255
|
+
`Cannot derive a spending key: signature is ${sigBytes.length} bytes, expected at least ${MIN_SIGNATURE_BYTES}. The wallet did not return a signature.`
|
|
4256
|
+
);
|
|
4257
|
+
}
|
|
4131
4258
|
}
|
|
4132
|
-
async function deriveMasterKeyBytes(signatureHex, chainId, address) {
|
|
4259
|
+
async function deriveMasterKeyBytes(signatureHex, chainId, address, version = "v2") {
|
|
4133
4260
|
const sigBytes = fromHex(signatureHex);
|
|
4134
|
-
|
|
4261
|
+
assertUsableSignature(sigBytes);
|
|
4262
|
+
const info = new TextEncoder().encode(
|
|
4263
|
+
`orbinum-sk-${version}:${chainId}:${address.toLowerCase()}`
|
|
4264
|
+
);
|
|
4135
4265
|
return (0, import_hkdf2.hkdf)(import_sha24.sha256, sigBytes, new Uint8Array(0), info, 32);
|
|
4136
4266
|
}
|
|
4137
|
-
async function deriveSpendingKeyFromSignature(signatureHex, chainId, address) {
|
|
4138
|
-
const masterBytes = await deriveMasterKeyBytes(signatureHex, chainId, address);
|
|
4267
|
+
async function deriveSpendingKeyFromSignature(signatureHex, chainId, address, version = "v2") {
|
|
4268
|
+
const masterBytes = await deriveMasterKeyBytes(signatureHex, chainId, address, version);
|
|
4139
4269
|
const skBigint = BigInt(toHex(masterBytes)) % BABYJUB_SUBORDER;
|
|
4140
4270
|
return skBigint === 0n ? 1n : skBigint;
|
|
4141
4271
|
}
|
|
@@ -5352,6 +5482,7 @@ var import_polkadot_api4 = require("polkadot-api");
|
|
|
5352
5482
|
EvmExplorer,
|
|
5353
5483
|
KNOWN_PRECOMPILES,
|
|
5354
5484
|
Keccak256,
|
|
5485
|
+
MIN_SIGNATURE_BYTES,
|
|
5355
5486
|
NoteBuilder,
|
|
5356
5487
|
OrbinumClient,
|
|
5357
5488
|
OrbinumClientProvider,
|
|
@@ -5360,6 +5491,8 @@ var import_polkadot_api4 = require("polkadot-api");
|
|
|
5360
5491
|
PrivacyModule,
|
|
5361
5492
|
RelayerStatusModule,
|
|
5362
5493
|
SLIP0044_NAMESPACE,
|
|
5494
|
+
SPENDING_KEY_VERIFYING_CONTRACT,
|
|
5495
|
+
SPENDING_KEY_WARNING,
|
|
5363
5496
|
ShieldedPoolModule,
|
|
5364
5497
|
ShieldedPoolPrecompile,
|
|
5365
5498
|
SignatureScheme,
|
|
@@ -5392,6 +5525,8 @@ var import_polkadot_api4 = require("polkadot-api");
|
|
|
5392
5525
|
deriveSelfEphSk,
|
|
5393
5526
|
deriveSpendingKeyFromSignature,
|
|
5394
5527
|
deriveSpendingKeyMessage,
|
|
5528
|
+
deriveSpendingKeyMessageV2,
|
|
5529
|
+
deriveSpendingKeyTypedData,
|
|
5395
5530
|
deriveStealthOwnerPk,
|
|
5396
5531
|
deriveStealthSk,
|
|
5397
5532
|
deriveVaultBlindKey,
|
package/dist/index.mjs
CHANGED
|
@@ -90,6 +90,27 @@ var SubstrateClient = class _SubstrateClient {
|
|
|
90
90
|
_httpUrl;
|
|
91
91
|
_dynamicBuilder = null;
|
|
92
92
|
_extDecoder = null;
|
|
93
|
+
_inflightTxCount = 0;
|
|
94
|
+
/**
|
|
95
|
+
* `true` while any submitted transaction is still waiting for finalization.
|
|
96
|
+
* Connection managers use this to defer destroying the client — killing the
|
|
97
|
+
* WS mid-submit rejects the pending tx with "Client destroyed" even though
|
|
98
|
+
* it may still land on-chain.
|
|
99
|
+
*
|
|
100
|
+
* Only covers promise-based submits (`submit`, `submitUnsignedAndWatch`,
|
|
101
|
+
* `signAndSubmit`); observable-based `submitAndWatch` callers are not tracked.
|
|
102
|
+
*/
|
|
103
|
+
get hasInflightTx() {
|
|
104
|
+
return this._inflightTxCount > 0;
|
|
105
|
+
}
|
|
106
|
+
async trackTx(p) {
|
|
107
|
+
this._inflightTxCount++;
|
|
108
|
+
try {
|
|
109
|
+
return await p;
|
|
110
|
+
} finally {
|
|
111
|
+
this._inflightTxCount--;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
93
114
|
/**
|
|
94
115
|
* Connects to the Orbinum node via WebSocket.
|
|
95
116
|
* Throws if the node does not respond within `timeoutMs`.
|
|
@@ -302,7 +323,7 @@ var SubstrateClient = class _SubstrateClient {
|
|
|
302
323
|
* Submits a pre-signed extrinsic (hex string) and waits for finalization.
|
|
303
324
|
*/
|
|
304
325
|
async submit(signedHex) {
|
|
305
|
-
return this._papi.submit(Binary.fromHex(signedHex));
|
|
326
|
+
return this.trackTx(this._papi.submit(Binary.fromHex(signedHex)));
|
|
306
327
|
}
|
|
307
328
|
/**
|
|
308
329
|
* Submits a pre-signed extrinsic and returns an Observable of tx lifecycle events.
|
|
@@ -317,14 +338,14 @@ var SubstrateClient = class _SubstrateClient {
|
|
|
317
338
|
* The bare tx bytes are produced by `tx.getBareTx()` from polkadot-api.
|
|
318
339
|
*/
|
|
319
340
|
async submitUnsignedAndWatch(bareTx) {
|
|
320
|
-
return this._papi.submit(bareTx);
|
|
341
|
+
return this.trackTx(this._papi.submit(bareTx));
|
|
321
342
|
}
|
|
322
343
|
/**
|
|
323
344
|
* Convenience: wrap raw call bytes and sign+submit in one step.
|
|
324
345
|
*/
|
|
325
346
|
async signAndSubmit(callData, signer) {
|
|
326
347
|
const tx = await this.txFromCallData(callData);
|
|
327
|
-
return tx.signAndSubmit(signer);
|
|
348
|
+
return this.trackTx(tx.signAndSubmit(signer));
|
|
328
349
|
}
|
|
329
350
|
/** Closes the WebSocket connection. */
|
|
330
351
|
destroy() {
|
|
@@ -581,17 +602,46 @@ var EvmClient = class {
|
|
|
581
602
|
}
|
|
582
603
|
return json.result ?? null;
|
|
583
604
|
}
|
|
605
|
+
/**
|
|
606
|
+
* Fetches a transaction by hash, or `null` when the node no longer knows it
|
|
607
|
+
* (never mined and evicted from the pool). Unlike `request`, a `null`
|
|
608
|
+
* result is a valid answer here, not an error.
|
|
609
|
+
*/
|
|
610
|
+
async getTransactionByHash(txHash) {
|
|
611
|
+
const res = await postJsonWithRetry(
|
|
612
|
+
this.rpcUrl,
|
|
613
|
+
JSON.stringify({
|
|
614
|
+
id: 1,
|
|
615
|
+
jsonrpc: "2.0",
|
|
616
|
+
method: "eth_getTransactionByHash",
|
|
617
|
+
params: [txHash]
|
|
618
|
+
})
|
|
619
|
+
);
|
|
620
|
+
if (!res.ok) throw new Error(`EVM HTTP ${res.status}: ${res.statusText}`);
|
|
621
|
+
const json = await res.json();
|
|
622
|
+
if (json.error) {
|
|
623
|
+
throw new Error(`EVM RPC [${json.error.code}]: ${json.error.message}`);
|
|
624
|
+
}
|
|
625
|
+
return json.result ?? null;
|
|
626
|
+
}
|
|
584
627
|
/**
|
|
585
628
|
* Polls `eth_getTransactionReceipt` until the transaction is included in a block.
|
|
586
629
|
*
|
|
630
|
+
* After `timeoutMs`, the tx-pool is consulted: a tx no longer known to the
|
|
631
|
+
* node is reported as dropped (safe to retry), while a tx still in the pool
|
|
632
|
+
* gets an extended grace window (up to 4× `timeoutMs`) before a "still
|
|
633
|
+
* pending" error — it may confirm later, so callers must NOT blindly retry.
|
|
634
|
+
*
|
|
587
635
|
* @param txHash - The transaction hash to wait for.
|
|
588
636
|
* @param intervalMs - Polling interval in milliseconds (default: 500).
|
|
589
637
|
* @param timeoutMs - Maximum time to wait in milliseconds (default: 60_000).
|
|
590
638
|
* @returns The transaction receipt once mined.
|
|
591
|
-
* @throws If the transaction is
|
|
639
|
+
* @throws If the transaction dropped, is still pending after the grace window, or reverted (`status == 0x0`).
|
|
592
640
|
*/
|
|
593
641
|
async waitForReceipt(txHash, intervalMs = 500, timeoutMs = 6e4) {
|
|
594
|
-
const
|
|
642
|
+
const start = Date.now();
|
|
643
|
+
const hardDeadline = start + timeoutMs * 4;
|
|
644
|
+
let deadline = start + timeoutMs;
|
|
595
645
|
while (Date.now() < deadline) {
|
|
596
646
|
const receipt = await this.getTransactionReceipt(txHash);
|
|
597
647
|
if (receipt !== null) {
|
|
@@ -602,10 +652,7 @@ var EvmClient = class {
|
|
|
602
652
|
if (!revertDetail) {
|
|
603
653
|
try {
|
|
604
654
|
const blockParam = receipt["blockNumber"] ?? "latest";
|
|
605
|
-
const rawTx = await this.
|
|
606
|
-
"eth_getTransactionByHash",
|
|
607
|
-
[txHash]
|
|
608
|
-
).catch(() => null);
|
|
655
|
+
const rawTx = await this.getTransactionByHash(txHash).catch(() => null);
|
|
609
656
|
if (rawTx) {
|
|
610
657
|
const calldata = rawTx["input"] ?? rawTx["data"];
|
|
611
658
|
if (calldata) {
|
|
@@ -628,8 +675,19 @@ var EvmClient = class {
|
|
|
628
675
|
return receipt;
|
|
629
676
|
}
|
|
630
677
|
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
678
|
+
if (Date.now() >= deadline && Date.now() < hardDeadline) {
|
|
679
|
+
const known = await this.getTransactionByHash(txHash).catch(() => void 0);
|
|
680
|
+
if (known === null) {
|
|
681
|
+
throw new Error(
|
|
682
|
+
`Transaction dropped from the tx pool (not mined within ${Date.now() - start}ms): ${txHash}`
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
deadline = Math.min(deadline + timeoutMs, hardDeadline);
|
|
686
|
+
}
|
|
631
687
|
}
|
|
632
|
-
throw new Error(
|
|
688
|
+
throw new Error(
|
|
689
|
+
`Transaction still pending after ${Date.now() - start}ms: ${txHash} \u2014 it may still confirm; check the hash on the explorer before retrying`
|
|
690
|
+
);
|
|
633
691
|
}
|
|
634
692
|
};
|
|
635
693
|
|
|
@@ -3236,7 +3294,7 @@ var DEFAULT_HEARTBEAT_TIMEOUT_MS = 4e3;
|
|
|
3236
3294
|
var DEFAULT_RECONNECT_BASE_MS = 3e3;
|
|
3237
3295
|
var DEFAULT_RECONNECT_MAX_MS = 3e4;
|
|
3238
3296
|
var DEFAULT_STABLE_AFTER_MS = 1e4;
|
|
3239
|
-
var OrbinumClientProvider = class {
|
|
3297
|
+
var OrbinumClientProvider = class _OrbinumClientProvider {
|
|
3240
3298
|
config;
|
|
3241
3299
|
connectTimeoutMs;
|
|
3242
3300
|
heartbeatIntervalMs;
|
|
@@ -3253,6 +3311,7 @@ var OrbinumClientProvider = class {
|
|
|
3253
3311
|
_reconnectTimer = null;
|
|
3254
3312
|
_stableTimer = null;
|
|
3255
3313
|
_reconnectAttempt = 0;
|
|
3314
|
+
_probeFailures = 0;
|
|
3256
3315
|
// ─── Events ─────────────────────────────────────────────────────────────
|
|
3257
3316
|
_listeners = /* @__PURE__ */ new Set();
|
|
3258
3317
|
/** Creates a new provider with the given configuration. Does not connect automatically — call `connect()` to initiate. */
|
|
@@ -3385,13 +3444,36 @@ var OrbinumClientProvider = class {
|
|
|
3385
3444
|
}
|
|
3386
3445
|
}
|
|
3387
3446
|
// ─── Heartbeat ──────────────────────────────────────────────────────────
|
|
3447
|
+
/**
|
|
3448
|
+
* Consecutive failed probes required before the client is torn down. A
|
|
3449
|
+
* single missed probe is routine (throttled background tab, node busy
|
|
3450
|
+
* verifying a ZK proof, transient network blip) — destroying the client on
|
|
3451
|
+
* it rejects every in-flight request with "Client destroyed" even though
|
|
3452
|
+
* the tx may still land on-chain.
|
|
3453
|
+
*/
|
|
3454
|
+
static PROBE_FAILURE_THRESHOLD = 2;
|
|
3455
|
+
/**
|
|
3456
|
+
* With a tx awaiting finalization, tolerate more missed probes: an unsigned
|
|
3457
|
+
* private_transfer/unshield makes the node CPU-bound on proof verification,
|
|
3458
|
+
* which is exactly when probes time out — tearing down then kills the very
|
|
3459
|
+
* tx being processed.
|
|
3460
|
+
*/
|
|
3461
|
+
static PROBE_FAILURE_THRESHOLD_INFLIGHT = 6;
|
|
3388
3462
|
/** Starts the periodic heartbeat loop. Replaces any existing timer. */
|
|
3389
3463
|
startHeartbeat() {
|
|
3390
3464
|
this.stopHeartbeat();
|
|
3465
|
+
this._probeFailures = 0;
|
|
3391
3466
|
this._heartbeatTimer = setInterval(async () => {
|
|
3392
3467
|
if (this._status !== "connected" || !this._orbinumClient) return;
|
|
3393
3468
|
const alive = await this.probe();
|
|
3394
|
-
if (
|
|
3469
|
+
if (this._status !== "connected") return;
|
|
3470
|
+
if (alive) {
|
|
3471
|
+
this._probeFailures = 0;
|
|
3472
|
+
return;
|
|
3473
|
+
}
|
|
3474
|
+
this._probeFailures++;
|
|
3475
|
+
const threshold = this._orbinumClient?.substrate.hasInflightTx ? _OrbinumClientProvider.PROBE_FAILURE_THRESHOLD_INFLIGHT : _OrbinumClientProvider.PROBE_FAILURE_THRESHOLD;
|
|
3476
|
+
if (this._probeFailures >= threshold) {
|
|
3395
3477
|
this.setStatus("disconnected", "Node is unreachable");
|
|
3396
3478
|
this.teardownClient();
|
|
3397
3479
|
this.scheduleReconnect();
|
|
@@ -3987,23 +4069,66 @@ function randomBlinding() {
|
|
|
3987
4069
|
return n === 0n ? 1n : n % BN254_R;
|
|
3988
4070
|
}
|
|
3989
4071
|
|
|
4072
|
+
// src/privacy-keys/SpendingKeyRequest.ts
|
|
4073
|
+
var SPENDING_KEY_VERIFYING_CONTRACT = PRECOMPILE_ADDR.SHIELDED_POOL;
|
|
4074
|
+
var SPENDING_KEY_WARNING = "Signing this grants full control of your Orbinum private funds. Only sign on the official Orbinum app.";
|
|
4075
|
+
function deriveSpendingKeyTypedData(chainId, address) {
|
|
4076
|
+
return {
|
|
4077
|
+
domain: {
|
|
4078
|
+
name: "Orbinum Shielded Pool",
|
|
4079
|
+
version: "2",
|
|
4080
|
+
chainId,
|
|
4081
|
+
verifyingContract: SPENDING_KEY_VERIFYING_CONTRACT
|
|
4082
|
+
},
|
|
4083
|
+
types: {
|
|
4084
|
+
SpendingKeyDerivation: [
|
|
4085
|
+
{ name: "warning", type: "string" },
|
|
4086
|
+
{ name: "account", type: "address" }
|
|
4087
|
+
]
|
|
4088
|
+
},
|
|
4089
|
+
primaryType: "SpendingKeyDerivation",
|
|
4090
|
+
message: {
|
|
4091
|
+
warning: SPENDING_KEY_WARNING,
|
|
4092
|
+
account: address.toLowerCase()
|
|
4093
|
+
}
|
|
4094
|
+
};
|
|
4095
|
+
}
|
|
4096
|
+
function deriveSpendingKeyMessageV2(chainId, address) {
|
|
4097
|
+
return `\u26A0 ${SPENDING_KEY_WARNING}
|
|
4098
|
+
|
|
4099
|
+
orbinum-spending-key-v2
|
|
4100
|
+
${chainId}
|
|
4101
|
+
${address.toLowerCase()}`;
|
|
4102
|
+
}
|
|
4103
|
+
function deriveSpendingKeyMessage(chainId, address) {
|
|
4104
|
+
return `orbinum-spending-key-v1
|
|
4105
|
+
${chainId}
|
|
4106
|
+
${address.toLowerCase()}`;
|
|
4107
|
+
}
|
|
4108
|
+
|
|
3990
4109
|
// src/privacy-keys/PrivacyKeys.ts
|
|
3991
4110
|
import { hkdf as hkdf2 } from "@noble/hashes/hkdf.js";
|
|
3992
4111
|
import { sha256 as sha2564 } from "@noble/hashes/sha2.js";
|
|
3993
4112
|
import { mulPointEscalar as mulPointEscalar4, Base8 as Base82, packPoint as packPoint3 } from "@zk-kit/baby-jubjub";
|
|
3994
4113
|
var IVK_DOMAIN = new TextEncoder().encode("orbinum-ivk-v1");
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
4114
|
+
var MIN_SIGNATURE_BYTES = 32;
|
|
4115
|
+
function assertUsableSignature(sigBytes) {
|
|
4116
|
+
if (sigBytes.length < MIN_SIGNATURE_BYTES) {
|
|
4117
|
+
throw new Error(
|
|
4118
|
+
`Cannot derive a spending key: signature is ${sigBytes.length} bytes, expected at least ${MIN_SIGNATURE_BYTES}. The wallet did not return a signature.`
|
|
4119
|
+
);
|
|
4120
|
+
}
|
|
3999
4121
|
}
|
|
4000
|
-
async function deriveMasterKeyBytes(signatureHex, chainId, address) {
|
|
4122
|
+
async function deriveMasterKeyBytes(signatureHex, chainId, address, version = "v2") {
|
|
4001
4123
|
const sigBytes = fromHex(signatureHex);
|
|
4002
|
-
|
|
4124
|
+
assertUsableSignature(sigBytes);
|
|
4125
|
+
const info = new TextEncoder().encode(
|
|
4126
|
+
`orbinum-sk-${version}:${chainId}:${address.toLowerCase()}`
|
|
4127
|
+
);
|
|
4003
4128
|
return hkdf2(sha2564, sigBytes, new Uint8Array(0), info, 32);
|
|
4004
4129
|
}
|
|
4005
|
-
async function deriveSpendingKeyFromSignature(signatureHex, chainId, address) {
|
|
4006
|
-
const masterBytes = await deriveMasterKeyBytes(signatureHex, chainId, address);
|
|
4130
|
+
async function deriveSpendingKeyFromSignature(signatureHex, chainId, address, version = "v2") {
|
|
4131
|
+
const masterBytes = await deriveMasterKeyBytes(signatureHex, chainId, address, version);
|
|
4007
4132
|
const skBigint = BigInt(toHex(masterBytes)) % BABYJUB_SUBORDER;
|
|
4008
4133
|
return skBigint === 0n ? 1n : skBigint;
|
|
4009
4134
|
}
|
|
@@ -5242,6 +5367,7 @@ export {
|
|
|
5242
5367
|
EvmExplorer,
|
|
5243
5368
|
KNOWN_PRECOMPILES,
|
|
5244
5369
|
Keccak256,
|
|
5370
|
+
MIN_SIGNATURE_BYTES,
|
|
5245
5371
|
NoteBuilder,
|
|
5246
5372
|
OrbinumClient,
|
|
5247
5373
|
OrbinumClientProvider,
|
|
@@ -5250,6 +5376,8 @@ export {
|
|
|
5250
5376
|
PrivacyModule,
|
|
5251
5377
|
RelayerStatusModule,
|
|
5252
5378
|
SLIP0044_NAMESPACE,
|
|
5379
|
+
SPENDING_KEY_VERIFYING_CONTRACT,
|
|
5380
|
+
SPENDING_KEY_WARNING,
|
|
5253
5381
|
ShieldedPoolModule,
|
|
5254
5382
|
ShieldedPoolPrecompile,
|
|
5255
5383
|
SignatureScheme,
|
|
@@ -5282,6 +5410,8 @@ export {
|
|
|
5282
5410
|
deriveSelfEphSk,
|
|
5283
5411
|
deriveSpendingKeyFromSignature,
|
|
5284
5412
|
deriveSpendingKeyMessage,
|
|
5413
|
+
deriveSpendingKeyMessageV2,
|
|
5414
|
+
deriveSpendingKeyTypedData,
|
|
5285
5415
|
deriveStealthOwnerPk,
|
|
5286
5416
|
deriveStealthSk,
|
|
5287
5417
|
deriveVaultBlindKey,
|