@orbinum/sdk 0.11.0 → 0.12.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 +178 -52
- package/dist/index.d.ts +178 -52
- package/dist/index.js +187 -59
- package/dist/index.mjs +191 -62
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1098,7 +1098,7 @@ var IndexerClient = class _IndexerClient {
|
|
|
1098
1098
|
/**
|
|
1099
1099
|
* Returns a paginated list of stealth scan hints ordered ascending by leafIndex.
|
|
1100
1100
|
* Each hint contains only the fields required for ECDH triage and decryption:
|
|
1101
|
-
* leafIndex, commitmentHex, assetId, ephPkHex, encryptedMemo.
|
|
1101
|
+
* leafIndex, commitmentHex, assetId, ephPkHex, encryptedMemo, circuitVersion.
|
|
1102
1102
|
*
|
|
1103
1103
|
* Use `sinceLeafIndex` for incremental scans (cursor = last seen leafIndex + 1).
|
|
1104
1104
|
*/
|
|
@@ -1518,8 +1518,8 @@ var BABYJUB_SUBORDER = 273603035897990940278080071815715938607681397215856725920
|
|
|
1518
1518
|
// src/shielded-pool/protocol/memo.ts
|
|
1519
1519
|
import { sha256 } from "@noble/hashes/sha2.js";
|
|
1520
1520
|
var KEY_DOMAIN = new TextEncoder().encode("orbinum-note-encryption-v1");
|
|
1521
|
-
var MEMO_PLAINTEXT_SIZE =
|
|
1522
|
-
function serializeMemo(value, ownerPk, blinding, assetId, counterpartyPk) {
|
|
1521
|
+
var MEMO_PLAINTEXT_SIZE = 120;
|
|
1522
|
+
function serializeMemo(value, ownerPk, blinding, assetId, counterpartyPk, circuitVersion) {
|
|
1523
1523
|
const buf = new Uint8Array(MEMO_PLAINTEXT_SIZE);
|
|
1524
1524
|
const view = new DataView(buf.buffer);
|
|
1525
1525
|
view.setBigUint64(0, value & 0xffffffffffffffffn, true);
|
|
@@ -1528,6 +1528,7 @@ function serializeMemo(value, ownerPk, blinding, assetId, counterpartyPk) {
|
|
|
1528
1528
|
buf.set(blinding.slice(0, 32), 48);
|
|
1529
1529
|
view.setUint32(80, assetId >>> 0, true);
|
|
1530
1530
|
buf.set(counterpartyPk.slice(0, 32), 84);
|
|
1531
|
+
view.setUint32(116, circuitVersion >>> 0, true);
|
|
1531
1532
|
return buf;
|
|
1532
1533
|
}
|
|
1533
1534
|
function deriveEncryptionKey(sharedSecret, commitment) {
|
|
@@ -1540,7 +1541,7 @@ function deriveEncryptionKey(sharedSecret, commitment) {
|
|
|
1540
1541
|
|
|
1541
1542
|
// src/shielded-pool/protocol/EncryptedMemo.ts
|
|
1542
1543
|
var NONCE_SIZE = 12;
|
|
1543
|
-
var CIPHERTEXT_SIZE =
|
|
1544
|
+
var CIPHERTEXT_SIZE = 136;
|
|
1544
1545
|
var EPH_PK_SIZE = 32;
|
|
1545
1546
|
var ENCRYPTED_MEMO_SIZE = NONCE_SIZE + CIPHERTEXT_SIZE + EPH_PK_SIZE;
|
|
1546
1547
|
function bytesToBjjScalar(bytes) {
|
|
@@ -1559,14 +1560,15 @@ function parsePlaintext(nonce, ciphertextWithMac, encKey) {
|
|
|
1559
1560
|
const blinding = bytesToBigintLE(plaintext.slice(48, 80));
|
|
1560
1561
|
const assetId = BigInt(view.getUint32(80, true));
|
|
1561
1562
|
const counterpartyPk = bytesToBigintLE(plaintext.slice(84, 116));
|
|
1562
|
-
|
|
1563
|
+
const circuitVersion = view.getUint32(116, true);
|
|
1564
|
+
return { value, ownerPk, blinding, assetId, counterpartyPk, circuitVersion };
|
|
1563
1565
|
} catch {
|
|
1564
1566
|
return null;
|
|
1565
1567
|
}
|
|
1566
1568
|
}
|
|
1567
1569
|
var EncryptedMemo = {
|
|
1568
1570
|
/**
|
|
1569
|
-
* Build and encrypt a memo for a note using ECDH (v2,
|
|
1571
|
+
* Build and encrypt a memo for a note using ECDH (v2, 180 bytes).
|
|
1570
1572
|
*
|
|
1571
1573
|
* @param value Note value in planck.
|
|
1572
1574
|
* @param ownerPk 32-byte owner public key (LE).
|
|
@@ -1578,11 +1580,20 @@ var EncryptedMemo = {
|
|
|
1578
1580
|
* decoded from a privacy address).
|
|
1579
1581
|
* Pass `new Uint8Array(32)` (all zeros) for a publicly-readable memo.
|
|
1580
1582
|
* @param counterpartyPk 32-byte counterparty BJJ Ax. Default: all zeros.
|
|
1581
|
-
* @
|
|
1583
|
+
* @param circuitVersion ZK circuit version the note is spent under. Default: 0.
|
|
1584
|
+
* @param ephSkOverride 32-byte ephemeral secret key (stealth coordination). Optional.
|
|
1585
|
+
* @returns 180-byte encrypted memo: nonce(12) || ciphertext+MAC(136) || ephPk(32).
|
|
1582
1586
|
*/
|
|
1583
|
-
encrypt(value, ownerPk, blinding, assetId, commitment, recipientIvkPacked, counterpartyPk = new Uint8Array(32), ephSkOverride) {
|
|
1587
|
+
encrypt(value, ownerPk, blinding, assetId, commitment, recipientIvkPacked, counterpartyPk = new Uint8Array(32), circuitVersion = 0, ephSkOverride) {
|
|
1584
1588
|
const nonce = randomBytes(NONCE_SIZE);
|
|
1585
|
-
const plaintext = serializeMemo(
|
|
1589
|
+
const plaintext = serializeMemo(
|
|
1590
|
+
value,
|
|
1591
|
+
ownerPk,
|
|
1592
|
+
blinding,
|
|
1593
|
+
assetId,
|
|
1594
|
+
counterpartyPk,
|
|
1595
|
+
circuitVersion
|
|
1596
|
+
);
|
|
1586
1597
|
const isZeroKey = recipientIvkPacked.every((b) => b === 0);
|
|
1587
1598
|
let sharedSecret;
|
|
1588
1599
|
let ephPkPackedBytes;
|
|
@@ -1613,29 +1624,31 @@ var EncryptedMemo = {
|
|
|
1613
1624
|
return result;
|
|
1614
1625
|
},
|
|
1615
1626
|
/**
|
|
1616
|
-
* Returns a
|
|
1627
|
+
* Returns a 180-byte public memo encrypted with a zero viewing key.
|
|
1617
1628
|
* Decryptable by anyone with `decrypt(memo, commitment, new Uint8Array(32))`.
|
|
1618
1629
|
* Convenience alias for `encrypt(..., new Uint8Array(32))`.
|
|
1619
1630
|
*/
|
|
1620
|
-
encryptPublic(value, ownerPk, blinding, assetId, commitment) {
|
|
1631
|
+
encryptPublic(value, ownerPk, blinding, assetId, commitment, circuitVersion = 0) {
|
|
1621
1632
|
return EncryptedMemo.encrypt(
|
|
1622
1633
|
value,
|
|
1623
1634
|
ownerPk,
|
|
1624
1635
|
blinding,
|
|
1625
1636
|
assetId,
|
|
1626
1637
|
commitment,
|
|
1627
|
-
new Uint8Array(32)
|
|
1638
|
+
new Uint8Array(32),
|
|
1639
|
+
new Uint8Array(32),
|
|
1640
|
+
circuitVersion
|
|
1628
1641
|
);
|
|
1629
1642
|
},
|
|
1630
1643
|
/**
|
|
1631
|
-
* Returns a
|
|
1644
|
+
* Returns a 180-byte zeroed dummy memo (no information, always valid on-chain).
|
|
1632
1645
|
*/
|
|
1633
1646
|
dummy() {
|
|
1634
1647
|
return new Uint8Array(ENCRYPTED_MEMO_SIZE);
|
|
1635
1648
|
},
|
|
1636
1649
|
/**
|
|
1637
1650
|
* Validates that `bytes` is a properly-sized encrypted memo.
|
|
1638
|
-
* Throws an Error if the length is not ENCRYPTED_MEMO_SIZE (
|
|
1651
|
+
* Throws an Error if the length is not ENCRYPTED_MEMO_SIZE (180 bytes).
|
|
1639
1652
|
*
|
|
1640
1653
|
* Call this at system boundaries (extrinsic builders, precompile encoders)
|
|
1641
1654
|
* to catch malformed memos before they reach the chain and fail on-chain.
|
|
@@ -1656,7 +1669,7 @@ var EncryptedMemo = {
|
|
|
1656
1669
|
* Returns null if decryption fails — wrong key, bad MAC, or malformed memo.
|
|
1657
1670
|
* Never throws; safe for scan loops.
|
|
1658
1671
|
*
|
|
1659
|
-
* @param memoBytes
|
|
1672
|
+
* @param memoBytes 180-byte encrypted memo.
|
|
1660
1673
|
* @param commitment 32-byte note commitment (LE).
|
|
1661
1674
|
* @param viewingSecretKey 32-byte HKDF viewing secret key from deriveViewingSecretKey().
|
|
1662
1675
|
*/
|
|
@@ -1668,13 +1681,13 @@ var EncryptedMemo = {
|
|
|
1668
1681
|
* Extract the ECDH shared secret from an encrypted memo using the recipient's viewing secret key.
|
|
1669
1682
|
*
|
|
1670
1683
|
* Used by NoteDecryptor to obtain the shared secret needed for stealth address derivation
|
|
1671
|
-
* without re-running the full decrypt path. Safe to call on any
|
|
1684
|
+
* without re-running the full decrypt path. Safe to call on any 180-byte memo.
|
|
1672
1685
|
*
|
|
1673
1686
|
* Returns `new Uint8Array(32)` (all zeros) for public/dummy memos (zero ephPk).
|
|
1674
1687
|
* Returns `null` if the memo is malformed or the ephPk is not a valid BJJ point.
|
|
1675
1688
|
* Never throws; safe for scan loops.
|
|
1676
1689
|
*
|
|
1677
|
-
* @param memoBytes
|
|
1690
|
+
* @param memoBytes 180-byte encrypted memo.
|
|
1678
1691
|
* @param viewingSecretKey 32-byte HKDF viewing secret key from deriveViewingSecretKey().
|
|
1679
1692
|
*/
|
|
1680
1693
|
extractSharedSecret(memoBytes, viewingSecretKey) {
|
|
@@ -1878,7 +1891,7 @@ var ShieldedPoolModule = class {
|
|
|
1878
1891
|
* Withdraws tokens from the shielded pool to a public address.
|
|
1879
1892
|
* Submits as an UNSIGNED (gasless) transaction — fee is embedded in the ZK proof.
|
|
1880
1893
|
* Pass a `signer` to fall back to signed submission (e.g. for testing).
|
|
1881
|
-
* Extrinsic: shieldedPool.unshield(proof, merkleRoot, nullifier, assetId, amount, recipient, fee)
|
|
1894
|
+
* Extrinsic: shieldedPool.unshield(proof, merkleRoot, nullifier, assetId, amount, recipient, fee, changeCommitment, changeEncryptedMemo, relayer, circuitVersion)
|
|
1882
1895
|
*/
|
|
1883
1896
|
async unshield(params, signer, txOptions) {
|
|
1884
1897
|
const entry = resolveTx(this.substrate.unsafe, "ShieldedPool", "unshield");
|
|
@@ -1902,8 +1915,9 @@ var ShieldedPoolModule = class {
|
|
|
1902
1915
|
fee: params.fee ?? 0n,
|
|
1903
1916
|
change_commitment: changeCommitment,
|
|
1904
1917
|
change_encrypted_memo: changeEncryptedMemo,
|
|
1905
|
-
relayer: void 0
|
|
1918
|
+
relayer: void 0,
|
|
1906
1919
|
// Option<H160> — None for direct Substrate submissions
|
|
1920
|
+
circuit_version: params.circuitVersion
|
|
1907
1921
|
});
|
|
1908
1922
|
if (signer) {
|
|
1909
1923
|
return toTxResult(
|
|
@@ -1916,7 +1930,7 @@ var ShieldedPoolModule = class {
|
|
|
1916
1930
|
* Performs a private (shielded) transfer between two notes.
|
|
1917
1931
|
* Submits as an UNSIGNED (gasless) transaction — fee is embedded in the ZK proof.
|
|
1918
1932
|
* Pass a `signer` to fall back to signed submission (e.g. for testing).
|
|
1919
|
-
* Extrinsic: shieldedPool.privateTransfer(proof, merkleRoot, nullifiers, commitments, memos, assetId, fee)
|
|
1933
|
+
* Extrinsic: shieldedPool.privateTransfer(proof, merkleRoot, nullifiers, commitments, memos, assetId, fee, relayer, circuitVersion)
|
|
1920
1934
|
*/
|
|
1921
1935
|
async privateTransfer(params, signer, txOptions) {
|
|
1922
1936
|
const nullifiers = params.inputs.map((inp) => inp.nullifier);
|
|
@@ -1937,8 +1951,9 @@ var ShieldedPoolModule = class {
|
|
|
1937
1951
|
encrypted_memos: memos,
|
|
1938
1952
|
asset_id: params.assetId,
|
|
1939
1953
|
fee: params.fee ?? 0n,
|
|
1940
|
-
relayer: void 0
|
|
1954
|
+
relayer: void 0,
|
|
1941
1955
|
// Option<H160> — None for direct Substrate submissions
|
|
1956
|
+
circuit_version: params.circuitVersion
|
|
1942
1957
|
});
|
|
1943
1958
|
if (signer) {
|
|
1944
1959
|
return toTxResult(
|
|
@@ -1972,7 +1987,7 @@ var ShieldedPoolModule = class {
|
|
|
1972
1987
|
* This is a SIGNED transaction — the relayer must sign it with their wallet.
|
|
1973
1988
|
* Before calling this, generate a ZK value proof with generateFeeClaimProof() (not yet implemented).
|
|
1974
1989
|
*
|
|
1975
|
-
* Extrinsic: shieldedPool.claim_shielded_fees(commitment, amount, asset_id, memo, proof, public_signals)
|
|
1990
|
+
* Extrinsic: shieldedPool.claim_shielded_fees(commitment, amount, asset_id, memo, proof, public_signals, circuit_version)
|
|
1976
1991
|
*/
|
|
1977
1992
|
async claimShieldedFees(params, signer, txOptions) {
|
|
1978
1993
|
EncryptedMemo.validate(params.encryptedMemo, "claimShieldedFees.encryptedMemo");
|
|
@@ -1983,7 +1998,8 @@ var ShieldedPoolModule = class {
|
|
|
1983
1998
|
asset_id: params.assetId,
|
|
1984
1999
|
encrypted_memo: params.encryptedMemo,
|
|
1985
2000
|
proof: params.proof,
|
|
1986
|
-
public_signals: params.publicSignals
|
|
2001
|
+
public_signals: params.publicSignals,
|
|
2002
|
+
circuit_version: params.circuitVersion
|
|
1987
2003
|
});
|
|
1988
2004
|
return toTxResult(
|
|
1989
2005
|
await (txOptions !== void 0 ? tx.signAndSubmit(signer, txOptions) : tx.signAndSubmit(signer))
|
|
@@ -2501,6 +2517,77 @@ var ZkVerifierModule = class {
|
|
|
2501
2517
|
}
|
|
2502
2518
|
};
|
|
2503
2519
|
|
|
2520
|
+
// src/shielded-pool/CircuitVersionResolver.ts
|
|
2521
|
+
import {
|
|
2522
|
+
WebArtifactProvider,
|
|
2523
|
+
circuitTypeToId
|
|
2524
|
+
} from "@orbinum/proof-generator";
|
|
2525
|
+
var CircuitVersionResolver = class {
|
|
2526
|
+
constructor(zkVerifier, baseUrl, providerFactory) {
|
|
2527
|
+
this.zkVerifier = zkVerifier;
|
|
2528
|
+
this.makeProvider = providerFactory ?? ((circuit, noteVersion) => new WebArtifactProvider({
|
|
2529
|
+
...baseUrl ? { baseUrl } : {},
|
|
2530
|
+
circuitVersions: { [circuit]: noteVersion }
|
|
2531
|
+
}));
|
|
2532
|
+
}
|
|
2533
|
+
zkVerifier;
|
|
2534
|
+
makeProvider;
|
|
2535
|
+
/**
|
|
2536
|
+
* Resolves the prover + on-chain version for spending a note of `circuit`
|
|
2537
|
+
* created under `noteVersion`. Fail-closed: throws on unsupported version or
|
|
2538
|
+
* VK-hash mismatch (CDN vs chain), before generating any proof.
|
|
2539
|
+
*/
|
|
2540
|
+
async resolve(circuit, noteVersion) {
|
|
2541
|
+
if (!Number.isInteger(noteVersion) || noteVersion <= 0) {
|
|
2542
|
+
throw new Error(
|
|
2543
|
+
`CircuitVersionResolver: invalid note circuitVersion ${noteVersion} for ${circuit}`
|
|
2544
|
+
);
|
|
2545
|
+
}
|
|
2546
|
+
const provider = this.makeProvider(circuit, noteVersion);
|
|
2547
|
+
const resolved = await provider.getResolvedVersion(circuit);
|
|
2548
|
+
if (resolved.version !== noteVersion) {
|
|
2549
|
+
throw new Error(
|
|
2550
|
+
`CircuitVersionResolver: prover resolved ${circuit} to v${resolved.version}, expected v${noteVersion}`
|
|
2551
|
+
);
|
|
2552
|
+
}
|
|
2553
|
+
const circuitId = circuitTypeToId(circuit);
|
|
2554
|
+
const info = await this.zkVerifier.getCircuitVersionInfo(circuitId);
|
|
2555
|
+
if (!info) {
|
|
2556
|
+
throw new Error(
|
|
2557
|
+
`CircuitVersionResolver: chain reports no version info for ${circuit} (id ${circuitId})`
|
|
2558
|
+
);
|
|
2559
|
+
}
|
|
2560
|
+
if (!info.supportedVersions.includes(noteVersion)) {
|
|
2561
|
+
throw new Error(
|
|
2562
|
+
`CircuitVersionResolver: chain does not support ${circuit} v${noteVersion} (supported: ${info.supportedVersions.join(", ")})`
|
|
2563
|
+
);
|
|
2564
|
+
}
|
|
2565
|
+
const chainVk = info.vkHashes.find((h) => h.version === noteVersion);
|
|
2566
|
+
if (!chainVk) {
|
|
2567
|
+
throw new Error(
|
|
2568
|
+
`CircuitVersionResolver: chain has no VK hash for ${circuit} v${noteVersion}`
|
|
2569
|
+
);
|
|
2570
|
+
}
|
|
2571
|
+
if (!vkHashEquals(chainVk.vkHash, resolved.vkHash)) {
|
|
2572
|
+
throw new Error(
|
|
2573
|
+
`CircuitVersionResolver: VK hash mismatch for ${circuit} v${noteVersion} \u2014 prover ${resolved.vkHash}, chain ${chainVk.vkHash}`
|
|
2574
|
+
);
|
|
2575
|
+
}
|
|
2576
|
+
return { provider, version: noteVersion };
|
|
2577
|
+
}
|
|
2578
|
+
};
|
|
2579
|
+
function vkHashEquals(a, b) {
|
|
2580
|
+
const na = normalizeHash(a);
|
|
2581
|
+
const nb = normalizeHash(b);
|
|
2582
|
+
if (na === null || nb === null) return false;
|
|
2583
|
+
return na === nb;
|
|
2584
|
+
}
|
|
2585
|
+
function normalizeHash(h) {
|
|
2586
|
+
if (typeof h !== "string") return null;
|
|
2587
|
+
const s = h.toLowerCase().startsWith("0x") ? h.toLowerCase().slice(2) : h.toLowerCase();
|
|
2588
|
+
return /^[0-9a-f]{64}$/.test(s) ? s : null;
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2504
2591
|
// src/relayer/RelayerStatusModule.ts
|
|
2505
2592
|
var RelayerStatusModule = class {
|
|
2506
2593
|
constructor(substrate) {
|
|
@@ -2746,12 +2833,12 @@ var AM_SEL = {
|
|
|
2746
2833
|
var SP_SEL = {
|
|
2747
2834
|
// shield(uint32,bytes32,bytes) → 0x9feb22ea (payable, amount = msg.value)
|
|
2748
2835
|
SHIELD: new Uint8Array([159, 235, 34, 234]),
|
|
2749
|
-
// privateTransfer(bytes,bytes32,bytes32[],bytes32[],bytes[],uint32,uint256)
|
|
2750
|
-
PRIVATE_TRANSFER: new Uint8Array([
|
|
2751
|
-
// unshield(bytes,bytes32,bytes32,uint32,uint256,bytes32,uint256,bytes32)
|
|
2752
|
-
UNSHIELD: new Uint8Array([
|
|
2753
|
-
// claimShieldedFees(bytes32,uint256,uint32,bytes,bytes,bytes)
|
|
2754
|
-
CLAIM_SHIELDED_FEES: new Uint8Array([
|
|
2836
|
+
// privateTransfer(bytes,bytes32,bytes32[],bytes32[],bytes[],uint32,uint256,uint32) → 0x66ed2cd4
|
|
2837
|
+
PRIVATE_TRANSFER: new Uint8Array([102, 237, 44, 212]),
|
|
2838
|
+
// unshield(bytes,bytes32,bytes32,uint32,uint256,bytes32,uint256,bytes32,bytes,uint32) → 0x4e505348
|
|
2839
|
+
UNSHIELD: new Uint8Array([78, 80, 83, 72]),
|
|
2840
|
+
// claimShieldedFees(bytes32,uint256,uint32,bytes,bytes,bytes,uint32) → 0x88d9deba
|
|
2841
|
+
CLAIM_SHIELDED_FEES: new Uint8Array([136, 217, 222, 186])
|
|
2755
2842
|
};
|
|
2756
2843
|
var KNOWN_PRECOMPILES = {
|
|
2757
2844
|
// ── Ethereum standard (EIP) ─────────────────────────────────────────────
|
|
@@ -2795,9 +2882,9 @@ var KNOWN_PRECOMPILES = {
|
|
|
2795
2882
|
name: "ShieldedPool",
|
|
2796
2883
|
functions: {
|
|
2797
2884
|
"9feb22ea": "shield(uint32,bytes32,bytes)",
|
|
2798
|
-
"
|
|
2799
|
-
|
|
2800
|
-
"
|
|
2885
|
+
"66ed2cd4": "privateTransfer(bytes,bytes32,bytes32[],bytes32[],bytes[],uint32,uint256,uint32)",
|
|
2886
|
+
"4e505348": "unshield(bytes,bytes32,bytes32,uint32,uint256,bytes32,uint256,bytes32,bytes,uint32)",
|
|
2887
|
+
"88d9deba": "claimShieldedFees(bytes32,uint256,uint32,bytes,bytes,bytes,uint32)"
|
|
2801
2888
|
}
|
|
2802
2889
|
}
|
|
2803
2890
|
};
|
|
@@ -2851,7 +2938,8 @@ var ShieldedPoolPrecompile = class {
|
|
|
2851
2938
|
// ─── privateTransfer ───────────────────────────────────────────────────────
|
|
2852
2939
|
/**
|
|
2853
2940
|
* Returns the ABI-encoded calldata for
|
|
2854
|
-
* `privateTransfer(bytes, bytes32, bytes32[], bytes32[], bytes[], uint32, uint256)`.
|
|
2941
|
+
* `privateTransfer(bytes, bytes32, bytes32[], bytes32[], bytes[], uint32, uint256, uint32)`.
|
|
2942
|
+
* The trailing `uint32` is the circuit version the input notes were created under.
|
|
2855
2943
|
*/
|
|
2856
2944
|
buildPrivateTransferCalldata(params) {
|
|
2857
2945
|
const nullifiers = params.inputs.map((i) => fromHex(i.nullifier));
|
|
@@ -2872,7 +2960,8 @@ var ShieldedPoolPrecompile = class {
|
|
|
2872
2960
|
{ type: "bytes32[]", value: commitments },
|
|
2873
2961
|
{ type: "bytes[]", value: memos },
|
|
2874
2962
|
{ type: "uint", value: BigInt(params.assetId) },
|
|
2875
|
-
{ type: "uint", value: params.fee ?? 0n }
|
|
2963
|
+
{ type: "uint", value: params.fee ?? 0n },
|
|
2964
|
+
{ type: "uint", value: BigInt(params.circuitVersion) }
|
|
2876
2965
|
);
|
|
2877
2966
|
}
|
|
2878
2967
|
/**
|
|
@@ -2913,7 +3002,8 @@ var ShieldedPoolPrecompile = class {
|
|
|
2913
3002
|
{ type: "bytes32", value: recipientBytes },
|
|
2914
3003
|
{ type: "uint", value: params.fee ?? 0n },
|
|
2915
3004
|
{ type: "bytes32", value: changeCommitment },
|
|
2916
|
-
{ type: "bytes", value: changeEncryptedMemo }
|
|
3005
|
+
{ type: "bytes", value: changeEncryptedMemo },
|
|
3006
|
+
{ type: "uint", value: BigInt(params.circuitVersion) }
|
|
2917
3007
|
);
|
|
2918
3008
|
}
|
|
2919
3009
|
/**
|
|
@@ -2963,7 +3053,7 @@ var ShieldedPoolPrecompile = class {
|
|
|
2963
3053
|
// ─── claimShieldedFees ───────────────────────────────────────────────────────────────────
|
|
2964
3054
|
/**
|
|
2965
3055
|
* Returns the ABI-encoded calldata for
|
|
2966
|
-
* `claimShieldedFees(bytes32,uint256,uint32,bytes,bytes,bytes)`.
|
|
3056
|
+
* `claimShieldedFees(bytes32,uint256,uint32,bytes,bytes,bytes,uint32)`.
|
|
2967
3057
|
*
|
|
2968
3058
|
* ABI layout (params after selector):
|
|
2969
3059
|
* - `commitment` — bytes32 (fixed)
|
|
@@ -2972,6 +3062,7 @@ var ShieldedPoolPrecompile = class {
|
|
|
2972
3062
|
* - `memo` — bytes (dynamic)
|
|
2973
3063
|
* - `proof` — bytes (dynamic, 128 bytes Groth16)
|
|
2974
3064
|
* - `publicSignals` — bytes (dynamic, 76 bytes)
|
|
3065
|
+
* - `circuitVersion` — uint32 (fixed, right-aligned)
|
|
2975
3066
|
*
|
|
2976
3067
|
* The validator identity is derived from `msg.sender` in the precompile —
|
|
2977
3068
|
* do NOT include it in the calldata.
|
|
@@ -2997,7 +3088,8 @@ var ShieldedPoolPrecompile = class {
|
|
|
2997
3088
|
{ type: "uint", value: BigInt(params.assetId) },
|
|
2998
3089
|
{ type: "bytes", value: params.encryptedMemo },
|
|
2999
3090
|
{ type: "bytes", value: params.proof },
|
|
3000
|
-
{ type: "bytes", value: params.publicSignals }
|
|
3091
|
+
{ type: "bytes", value: params.publicSignals },
|
|
3092
|
+
{ type: "uint", value: BigInt(params.circuitVersion) }
|
|
3001
3093
|
);
|
|
3002
3094
|
}
|
|
3003
3095
|
/**
|
|
@@ -3435,6 +3527,11 @@ var OrbinumClient = class _OrbinumClient {
|
|
|
3435
3527
|
chain;
|
|
3436
3528
|
/** Typed access to `zkVerifier_*` custom RPC endpoints. */
|
|
3437
3529
|
zkVerifier;
|
|
3530
|
+
/**
|
|
3531
|
+
* Resolves a note's circuit version to a pinned prover + on-chain version
|
|
3532
|
+
* before spending it (fail-closed: throws on unsupported version / VK mismatch).
|
|
3533
|
+
*/
|
|
3534
|
+
circuitVersionResolver;
|
|
3438
3535
|
/** Typed access to `relayer_*` RPC endpoints (registry lookup and pending fee queries). */
|
|
3439
3536
|
relayerStatus;
|
|
3440
3537
|
/**
|
|
@@ -3443,7 +3540,7 @@ var OrbinumClient = class _OrbinumClient {
|
|
|
3443
3540
|
*/
|
|
3444
3541
|
precompiles;
|
|
3445
3542
|
/** @internal Use `OrbinumClient.connect()` to obtain an instance. */
|
|
3446
|
-
constructor(substrate, evm, indexer) {
|
|
3543
|
+
constructor(substrate, evm, indexer, circuitsBaseUrl) {
|
|
3447
3544
|
this.substrate = substrate;
|
|
3448
3545
|
this.evm = evm;
|
|
3449
3546
|
this.evmExplorer = evm ? new EvmExplorer(evm) : null;
|
|
@@ -3453,6 +3550,7 @@ var OrbinumClient = class _OrbinumClient {
|
|
|
3453
3550
|
this.privacy = new PrivacyModule(substrate);
|
|
3454
3551
|
this.chain = new ChainModule(substrate);
|
|
3455
3552
|
this.zkVerifier = new ZkVerifierModule(substrate);
|
|
3553
|
+
this.circuitVersionResolver = new CircuitVersionResolver(this.zkVerifier, circuitsBaseUrl);
|
|
3456
3554
|
this.relayerStatus = new RelayerStatusModule(substrate);
|
|
3457
3555
|
this.precompiles = evm ? {
|
|
3458
3556
|
shieldedPool: new ShieldedPoolPrecompile(evm),
|
|
@@ -3473,7 +3571,7 @@ var OrbinumClient = class _OrbinumClient {
|
|
|
3473
3571
|
);
|
|
3474
3572
|
const evm = config.evmRpc ? new EvmClient(config.evmRpc) : null;
|
|
3475
3573
|
const indexer = config.indexerUrl ? new IndexerClient({ baseUrl: config.indexerUrl }) : null;
|
|
3476
|
-
return new _OrbinumClient(substrate, evm, indexer);
|
|
3574
|
+
return new _OrbinumClient(substrate, evm, indexer, config.circuitsBaseUrl);
|
|
3477
3575
|
}
|
|
3478
3576
|
/** Closes the underlying Substrate WebSocket connection and releases all resources. */
|
|
3479
3577
|
destroy() {
|
|
@@ -3595,6 +3693,8 @@ var OrbinumClientProvider = class {
|
|
|
3595
3693
|
};
|
|
3596
3694
|
if (this.config.evmRpc) connectConfig.evmRpc = this.config.evmRpc;
|
|
3597
3695
|
if (this.config.indexerUrl) connectConfig.indexerUrl = this.config.indexerUrl;
|
|
3696
|
+
if (this.config.circuitsBaseUrl)
|
|
3697
|
+
connectConfig.circuitsBaseUrl = this.config.circuitsBaseUrl;
|
|
3598
3698
|
const clientPromise = OrbinumClient.connect(connectConfig);
|
|
3599
3699
|
const client = await Promise.race([clientPromise, timeoutPromise]);
|
|
3600
3700
|
orphanClient = client;
|
|
@@ -3781,6 +3881,9 @@ var OrbinumClientProvider = class {
|
|
|
3781
3881
|
}
|
|
3782
3882
|
};
|
|
3783
3883
|
|
|
3884
|
+
// src/shielded-pool/protocol/types.ts
|
|
3885
|
+
var CURRENT_CIRCUIT_VERSION = 1;
|
|
3886
|
+
|
|
3784
3887
|
// src/utils/stealth.ts
|
|
3785
3888
|
import { sha256 as sha2562 } from "@noble/hashes/sha2.js";
|
|
3786
3889
|
import { hkdf } from "@noble/hashes/hkdf.js";
|
|
@@ -3889,6 +3992,7 @@ var NoteBuilder = class {
|
|
|
3889
3992
|
const blinding = input.blinding ?? BigInt(Date.now());
|
|
3890
3993
|
const spendingKey = input.spendingKey ?? 0n;
|
|
3891
3994
|
const counterpartyPk = input.counterpartyPk ?? 0n;
|
|
3995
|
+
const circuitVersion = input.circuitVersion ?? CURRENT_CIRCUIT_VERSION;
|
|
3892
3996
|
const useStealth = input.viewingPublicKey !== void 0 && input.recipientOwnerPk !== void 0;
|
|
3893
3997
|
let memo;
|
|
3894
3998
|
if (useStealth) {
|
|
@@ -3923,6 +4027,7 @@ var NoteBuilder = class {
|
|
|
3923
4027
|
stealthCommitmentBytes,
|
|
3924
4028
|
recipientIvkPacked,
|
|
3925
4029
|
bigintTo32Le(counterpartyPk),
|
|
4030
|
+
circuitVersion,
|
|
3926
4031
|
ephSk
|
|
3927
4032
|
)
|
|
3928
4033
|
);
|
|
@@ -3940,6 +4045,7 @@ var NoteBuilder = class {
|
|
|
3940
4045
|
ownerPk: effectiveOwnerPk,
|
|
3941
4046
|
blinding,
|
|
3942
4047
|
spendingKey,
|
|
4048
|
+
circuitVersion,
|
|
3943
4049
|
spent: false,
|
|
3944
4050
|
spentAt: null,
|
|
3945
4051
|
commitment: commitment2,
|
|
@@ -3962,7 +4068,8 @@ var NoteBuilder = class {
|
|
|
3962
4068
|
Number(assetId),
|
|
3963
4069
|
commitmentBytes,
|
|
3964
4070
|
input.viewingPublicKey,
|
|
3965
|
-
bigintTo32Le(counterpartyPk)
|
|
4071
|
+
bigintTo32Le(counterpartyPk),
|
|
4072
|
+
circuitVersion
|
|
3966
4073
|
)
|
|
3967
4074
|
) : Array.from(EncryptedMemo.dummy());
|
|
3968
4075
|
if (memo.length !== ENCRYPTED_MEMO_SIZE)
|
|
@@ -3975,6 +4082,7 @@ var NoteBuilder = class {
|
|
|
3975
4082
|
ownerPk,
|
|
3976
4083
|
blinding,
|
|
3977
4084
|
spendingKey,
|
|
4085
|
+
circuitVersion,
|
|
3978
4086
|
spent: false,
|
|
3979
4087
|
spentAt: null,
|
|
3980
4088
|
commitment,
|
|
@@ -3986,7 +4094,7 @@ var NoteBuilder = class {
|
|
|
3986
4094
|
};
|
|
3987
4095
|
}
|
|
3988
4096
|
/**
|
|
3989
|
-
* Build the
|
|
4097
|
+
* Build the 180-byte ECDH-encrypted memo for a note.
|
|
3990
4098
|
*
|
|
3991
4099
|
* Pure TypeScript implementation — no WASM dependency.
|
|
3992
4100
|
* Uses ChaCha20-Poly1305 with ECDH key agreement (BabyJubJub ephemeral keypair).
|
|
@@ -4005,7 +4113,8 @@ var NoteBuilder = class {
|
|
|
4005
4113
|
Number(note.assetId),
|
|
4006
4114
|
bigintTo32Le(note.commitment),
|
|
4007
4115
|
recipientIvkPacked ?? new Uint8Array(32),
|
|
4008
|
-
counterpartyPk ?? bigintTo32Le(note.counterpartyPk ?? 0n)
|
|
4116
|
+
counterpartyPk ?? bigintTo32Le(note.counterpartyPk ?? 0n),
|
|
4117
|
+
note.circuitVersion
|
|
4009
4118
|
);
|
|
4010
4119
|
}
|
|
4011
4120
|
};
|
|
@@ -4067,6 +4176,7 @@ function tryDecryptNoteVerbose(commitment, viewingSecretKey, spendingKey, ownOwn
|
|
|
4067
4176
|
ownerPk: effectiveOwnerPk,
|
|
4068
4177
|
blinding: plaintext.blinding,
|
|
4069
4178
|
spendingKey: effectiveSpendingKey,
|
|
4179
|
+
circuitVersion: plaintext.circuitVersion,
|
|
4070
4180
|
spent: false,
|
|
4071
4181
|
spentAt: null,
|
|
4072
4182
|
commitment: recomputed,
|
|
@@ -4140,7 +4250,7 @@ function selectNotes(notes, needed) {
|
|
|
4140
4250
|
for (let j = i + 1; j < sorted.length; j++) {
|
|
4141
4251
|
const a = sorted[i];
|
|
4142
4252
|
const b = sorted[j];
|
|
4143
|
-
if (a !== void 0 && b !== void 0 && a.value + b.value >= needed) {
|
|
4253
|
+
if (a !== void 0 && b !== void 0 && a.circuitVersion === b.circuitVersion && a.value + b.value >= needed) {
|
|
4144
4254
|
return [a, b];
|
|
4145
4255
|
}
|
|
4146
4256
|
}
|
|
@@ -4497,6 +4607,11 @@ async function encryptNote(key, blindKey, note) {
|
|
|
4497
4607
|
}
|
|
4498
4608
|
async function decryptNoteRecord(key, rec) {
|
|
4499
4609
|
const note = await decryptJson(key, rec.iv, rec.ciphertext);
|
|
4610
|
+
if (typeof note.circuitVersion !== "number") {
|
|
4611
|
+
throw new Error(
|
|
4612
|
+
"decryptNoteRecord: note is missing circuitVersion (invalid or corrupt record)"
|
|
4613
|
+
);
|
|
4614
|
+
}
|
|
4500
4615
|
return applyNoteStatus(note, {
|
|
4501
4616
|
...rec.spent !== void 0 && { spent: rec.spent },
|
|
4502
4617
|
spentAt: rec.spentAt ?? null
|
|
@@ -4508,9 +4623,9 @@ async function noteBlindTag(blindKey, hex) {
|
|
|
4508
4623
|
|
|
4509
4624
|
// src/proof-generator/unshield.ts
|
|
4510
4625
|
import {
|
|
4511
|
-
CircuitType,
|
|
4626
|
+
CircuitType as CircuitType2,
|
|
4512
4627
|
generateProof,
|
|
4513
|
-
WebArtifactProvider
|
|
4628
|
+
WebArtifactProvider as WebArtifactProvider2
|
|
4514
4629
|
} from "@orbinum/proof-generator";
|
|
4515
4630
|
import { randomBytes as randomBytes3 } from "@noble/ciphers/utils.js";
|
|
4516
4631
|
import { mulPointEscalar as mulPointEscalar6, Base8 as Base84 } from "@zk-kit/baby-jubjub";
|
|
@@ -4557,18 +4672,18 @@ async function generateUnshieldProof(inputs, options = {}) {
|
|
|
4557
4672
|
change_blinding: changeBlinding.toString(),
|
|
4558
4673
|
change_owner_pubkey: changeOwnerPubkey.toString()
|
|
4559
4674
|
};
|
|
4560
|
-
const provider = options.provider ?? new
|
|
4675
|
+
const provider = options.provider ?? new WebArtifactProvider2();
|
|
4561
4676
|
const opts = { provider };
|
|
4562
4677
|
if (options.verbose !== void 0) opts.verbose = options.verbose;
|
|
4563
|
-
const proofResult = await generateProof(
|
|
4678
|
+
const proofResult = await generateProof(CircuitType2.Unshield, circuitInputs, opts);
|
|
4564
4679
|
return { ...proofResult, changeCommitment, changeValue, changeBlinding, changeOwnerPubkey };
|
|
4565
4680
|
}
|
|
4566
4681
|
|
|
4567
4682
|
// src/proof-generator/transfer.ts
|
|
4568
4683
|
import {
|
|
4569
|
-
CircuitType as
|
|
4684
|
+
CircuitType as CircuitType3,
|
|
4570
4685
|
generateProof as generateProof2,
|
|
4571
|
-
WebArtifactProvider as
|
|
4686
|
+
WebArtifactProvider as WebArtifactProvider3
|
|
4572
4687
|
} from "@orbinum/proof-generator";
|
|
4573
4688
|
async function generateTransferProof(params, options = {}) {
|
|
4574
4689
|
const root = leHexToBigint(params.merkleRoot).toString();
|
|
@@ -4594,17 +4709,17 @@ async function generateTransferProof(params, options = {}) {
|
|
|
4594
4709
|
output_owner_pubkeys: [o0.ownerPk.toString(), o1.ownerPk.toString()],
|
|
4595
4710
|
output_blindings: [o0.blinding.toString(), o1.blinding.toString()]
|
|
4596
4711
|
};
|
|
4597
|
-
const provider = options.provider ?? new
|
|
4712
|
+
const provider = options.provider ?? new WebArtifactProvider3();
|
|
4598
4713
|
const opts = { provider };
|
|
4599
4714
|
if (options.verbose !== void 0) opts.verbose = options.verbose;
|
|
4600
|
-
return generateProof2(
|
|
4715
|
+
return generateProof2(CircuitType3.Transfer, circuitInputs, opts);
|
|
4601
4716
|
}
|
|
4602
4717
|
|
|
4603
4718
|
// src/proof-generator/fee-claim.ts
|
|
4604
4719
|
import {
|
|
4605
|
-
CircuitType as
|
|
4720
|
+
CircuitType as CircuitType4,
|
|
4606
4721
|
generateProof as generateProof3,
|
|
4607
|
-
WebArtifactProvider as
|
|
4722
|
+
WebArtifactProvider as WebArtifactProvider4
|
|
4608
4723
|
} from "@orbinum/proof-generator";
|
|
4609
4724
|
async function generateFeeClaimProof(inputs, options = {}) {
|
|
4610
4725
|
if (inputs.amount <= 0n) {
|
|
@@ -4617,10 +4732,10 @@ async function generateFeeClaimProof(inputs, options = {}) {
|
|
|
4617
4732
|
owner_pubkey: inputs.ownerPubkey.toString(),
|
|
4618
4733
|
blinding: inputs.blinding.toString()
|
|
4619
4734
|
};
|
|
4620
|
-
const provider = options.provider ?? new
|
|
4735
|
+
const provider = options.provider ?? new WebArtifactProvider4();
|
|
4621
4736
|
const opts = { provider };
|
|
4622
4737
|
if (options.verbose !== void 0) opts.verbose = options.verbose;
|
|
4623
|
-
const proofResult = await generateProof3(
|
|
4738
|
+
const proofResult = await generateProof3(CircuitType4.ValueProof, circuitInputs, opts);
|
|
4624
4739
|
const [sigCommitment, sigValue, sigAssetId, sigOwnerHash] = proofResult.publicSignals.map(BigInt);
|
|
4625
4740
|
const buf = new Uint8Array(76);
|
|
4626
4741
|
buf.set(bigintTo32Le(sigCommitment), 0);
|
|
@@ -4676,9 +4791,19 @@ function decodePrecompileCalldata(address, input) {
|
|
|
4676
4791
|
const recipient = toHex(data.slice(160, 192));
|
|
4677
4792
|
const fee = decodeUint(data, 192);
|
|
4678
4793
|
const changeCommitment = toHex(data.slice(224, 256));
|
|
4794
|
+
const circuitVersion = decodeUint(data, 288);
|
|
4679
4795
|
return {
|
|
4680
4796
|
fnSig,
|
|
4681
|
-
args: {
|
|
4797
|
+
args: {
|
|
4798
|
+
root,
|
|
4799
|
+
nullifier,
|
|
4800
|
+
assetId,
|
|
4801
|
+
amount,
|
|
4802
|
+
recipient,
|
|
4803
|
+
fee,
|
|
4804
|
+
changeCommitment,
|
|
4805
|
+
circuitVersion
|
|
4806
|
+
}
|
|
4682
4807
|
};
|
|
4683
4808
|
} catch {
|
|
4684
4809
|
return { fnSig, args: {} };
|
|
@@ -4694,7 +4819,8 @@ function decodePrecompileCalldata(address, input) {
|
|
|
4694
4819
|
const commitments = Number(decodeUint(data, commOffset));
|
|
4695
4820
|
const assetId = decodeUint(data, 160);
|
|
4696
4821
|
const fee = decodeUint(data, 192);
|
|
4697
|
-
|
|
4822
|
+
const circuitVersion = decodeUint(data, 224);
|
|
4823
|
+
return { fnSig, args: { root, nullifiers, commitments, assetId, fee, circuitVersion } };
|
|
4698
4824
|
} catch {
|
|
4699
4825
|
return { fnSig, args: {} };
|
|
4700
4826
|
}
|
|
@@ -4705,7 +4831,8 @@ function decodePrecompileCalldata(address, input) {
|
|
|
4705
4831
|
const commitment = toHex(data.slice(0, 32));
|
|
4706
4832
|
const amount = decodeUint(data, 32);
|
|
4707
4833
|
const assetId = decodeUint(data, 64);
|
|
4708
|
-
|
|
4834
|
+
const circuitVersion = decodeUint(data, 192);
|
|
4835
|
+
return { fnSig, args: { commitment, amount, assetId, circuitVersion } };
|
|
4709
4836
|
} catch {
|
|
4710
4837
|
return { fnSig, args: {} };
|
|
4711
4838
|
}
|
|
@@ -4717,8 +4844,8 @@ function decodePrecompileCalldata(address, input) {
|
|
|
4717
4844
|
var CircuitId = {
|
|
4718
4845
|
Transfer: 1,
|
|
4719
4846
|
Unshield: 2,
|
|
4720
|
-
|
|
4721
|
-
|
|
4847
|
+
PrivateLink: 5,
|
|
4848
|
+
ValueProof: 6
|
|
4722
4849
|
};
|
|
4723
4850
|
|
|
4724
4851
|
// src/utils/string.ts
|
|
@@ -5400,8 +5527,10 @@ export {
|
|
|
5400
5527
|
BABYJUB_SUBORDER,
|
|
5401
5528
|
BN254_R,
|
|
5402
5529
|
Blake2256,
|
|
5530
|
+
CURRENT_CIRCUIT_VERSION,
|
|
5403
5531
|
CircuitId,
|
|
5404
|
-
CircuitType,
|
|
5532
|
+
CircuitType2 as CircuitType,
|
|
5533
|
+
CircuitVersionResolver,
|
|
5405
5534
|
CryptoPrecompiles,
|
|
5406
5535
|
ENCRYPTED_MEMO_SIZE,
|
|
5407
5536
|
EncryptedMemo,
|
|
@@ -5424,7 +5553,7 @@ export {
|
|
|
5424
5553
|
Storage,
|
|
5425
5554
|
SubstrateClient,
|
|
5426
5555
|
VaultLockedError,
|
|
5427
|
-
WebArtifactProvider,
|
|
5556
|
+
WebArtifactProvider2 as WebArtifactProvider,
|
|
5428
5557
|
ZkVerifierModule,
|
|
5429
5558
|
accountIdHexToSs58,
|
|
5430
5559
|
addressToAccountIdHex,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orbinum/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Official TypeScript SDK for Orbinum.",
|
|
5
5
|
"author": "Orbinum",
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@noble/ciphers": "2.2.0",
|
|
49
49
|
"@noble/hashes": "2.2.0",
|
|
50
|
-
"@orbinum/proof-generator": "
|
|
50
|
+
"@orbinum/proof-generator": "4.0.0",
|
|
51
51
|
"@polkadot-api/metadata-builders": "0.14.2",
|
|
52
52
|
"@polkadot-api/substrate-bindings": "0.20.2",
|
|
53
53
|
"@polkadot-api/tx-utils": "0.3.2",
|