@cloak.dev/sdk 0.1.7 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +421 -278
- package/dist/chunk-YX5SCAMR.js +594 -0
- package/dist/index.cjs +6201 -1652
- package/dist/index.d.cts +2705 -447
- package/dist/index.d.ts +2705 -447
- package/dist/index.js +5729 -1609
- package/dist/{utxo-LSTVI4HH.js → utxo-PFJT3ETR.js} +7 -5
- package/package.json +4 -4
- package/CHANGELOG.md +0 -173
- package/dist/chunk-2SOX3JNO.js +0 -255
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
NATIVE_SOL_MINT,
|
|
3
|
-
bigintToBytes32,
|
|
3
|
+
bigintToBytes322 as bigintToBytes32,
|
|
4
4
|
bigintToHex,
|
|
5
|
-
computeCommitment,
|
|
6
|
-
computeNullifier,
|
|
5
|
+
computeCommitment2 as computeCommitment,
|
|
6
|
+
computeNullifier2 as computeNullifier,
|
|
7
7
|
computeSignature,
|
|
8
8
|
createUtxo,
|
|
9
9
|
createZeroUtxo,
|
|
@@ -11,14 +11,15 @@ import {
|
|
|
11
11
|
deriveUtxoKeypairFromSpendKey,
|
|
12
12
|
deserializeUtxo,
|
|
13
13
|
generateUtxoKeypair,
|
|
14
|
-
hexToBigint,
|
|
14
|
+
hexToBigint2 as hexToBigint,
|
|
15
15
|
pubkeyToFieldElement,
|
|
16
|
+
pubkeyToFieldLimbs,
|
|
16
17
|
randomFieldElement,
|
|
17
18
|
selectUtxos,
|
|
18
19
|
serializeUtxo,
|
|
19
20
|
sumUtxoAmounts,
|
|
20
21
|
utxoEquals
|
|
21
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-YX5SCAMR.js";
|
|
22
23
|
export {
|
|
23
24
|
NATIVE_SOL_MINT,
|
|
24
25
|
bigintToBytes32,
|
|
@@ -34,6 +35,7 @@ export {
|
|
|
34
35
|
generateUtxoKeypair,
|
|
35
36
|
hexToBigint,
|
|
36
37
|
pubkeyToFieldElement,
|
|
38
|
+
pubkeyToFieldLimbs,
|
|
37
39
|
randomFieldElement,
|
|
38
40
|
selectUtxos,
|
|
39
41
|
serializeUtxo,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloak.dev/sdk",
|
|
3
|
-
"description": "Shield, send, and swap on Solana privately
|
|
4
|
-
"version": "0.
|
|
3
|
+
"description": "Shield, send, and swap on Solana privately \u2014 TypeScript SDK with UTXO-based zero-knowledge transactions",
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
7
7
|
"module": "dist/index.js",
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"test:coverage": "NODE_OPTIONS=--experimental-vm-modules jest --coverage",
|
|
23
23
|
"test:verbose": "NODE_OPTIONS=--experimental-vm-modules jest --verbose",
|
|
24
24
|
"prepare": "npm run build",
|
|
25
|
-
"prepublishOnly": "npm run build",
|
|
25
|
+
"prepublishOnly": "npm run build && node scripts/verify-dist-matches-src.mjs",
|
|
26
|
+
"verify:dist": "node scripts/verify-dist-matches-src.mjs",
|
|
26
27
|
"example:fast-send": "tsx examples/fast-send.ts",
|
|
27
28
|
"example:fast-usdc-send": "tsx examples/fast-usdc-send.ts",
|
|
28
29
|
"example:usdc-pool-transfer": "tsx examples/usdc-pool-transfer.ts",
|
|
@@ -103,7 +104,6 @@
|
|
|
103
104
|
"files": [
|
|
104
105
|
"dist",
|
|
105
106
|
"README.md",
|
|
106
|
-
"CHANGELOG.md",
|
|
107
107
|
"LICENSE"
|
|
108
108
|
],
|
|
109
109
|
"publishConfig": {
|
package/CHANGELOG.md
DELETED
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to `@cloak.dev/sdk` are documented here. The format
|
|
4
|
-
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the
|
|
5
|
-
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
-
|
|
7
|
-
## [0.1.6] — 2026-04-27
|
|
8
|
-
|
|
9
|
-
### Removed (BREAKING)
|
|
10
|
-
|
|
11
|
-
- **`CloakSDK.deposit / privateTransfer / withdraw / send / swap`** —
|
|
12
|
-
these methods built a legacy `[discriminator: 1, amount: u64,
|
|
13
|
-
commitment: 32]` deposit instruction. The deployed shield-pool program
|
|
14
|
-
has repurposed discriminator `1` to `TransactSwap` (which requires
|
|
15
|
-
`>= 8` accounts), so every external invocation hit `0x1063
|
|
16
|
-
MissingAccounts` at ~112 CU. Reported by the cloak-squads hackathon
|
|
17
|
-
team on 2026-04-26.
|
|
18
|
-
- **`createDepositInstruction` / `validateDepositParams` /
|
|
19
|
-
`DepositInstructionParams`** — the disc-1 instruction builder and its
|
|
20
|
-
validators.
|
|
21
|
-
- **`CloakNote` model and helpers** — `generateNote`, `parseNote`,
|
|
22
|
-
`exportNote`, `findNoteByCommitment`, `filterNotesByNetwork`,
|
|
23
|
-
`filterWithdrawableNotes`, `isWithdrawable`, `updateNoteWithDeposit`,
|
|
24
|
-
`getRecipientAmount`, `generateNoteFromWallet`. The OLD
|
|
25
|
-
`withdraw_regular.circom` model used a 3-input
|
|
26
|
-
`Poseidon(amount, r0, r1, pk_spend)` commitment that is incompatible
|
|
27
|
-
with the deployed `transaction.circom` UTXO circuit (4-input
|
|
28
|
-
`Poseidon(amount, pubkey, blinding, mintAddress)`).
|
|
29
|
-
- **`CloakSDK.loadNotes / saveNote / findNote / getTransactionMetadata`**
|
|
30
|
-
and the `StorageAdapter.notes*` methods. The adapter is now keys-only.
|
|
31
|
-
- **Encrypted-note helpers** — `prepareEncryptedOutput`,
|
|
32
|
-
`prepareEncryptedOutputForRecipient`, `encodeNoteSimple` (legacy
|
|
33
|
-
`CloakNote`-based variants).
|
|
34
|
-
- **Pending-operations helpers** — `savePendingDeposit`/`Withdrawal`
|
|
35
|
-
and friends, plus `PendingDeposit` / `PendingWithdrawal` types.
|
|
36
|
-
- **Legacy proof generation** — `src/utils/proof-generation.ts`
|
|
37
|
-
(`generateWithdrawRegularProof`, `generateWithdrawSwapProof`,
|
|
38
|
-
`verifyCircuitIntegrity`, `verifyAllCircuits`,
|
|
39
|
-
`areCircuitsAvailable`, `getDefaultCircuitsPath`,
|
|
40
|
-
`EXPECTED_CIRCUIT_HASHES`, `DEFAULT_CIRCUITS_URL`).
|
|
41
|
-
- **Legacy bigint hash helpers** in `src/utils/crypto.ts` —
|
|
42
|
-
`computeCommitment` (3-arg), `generateCommitmentAsync`,
|
|
43
|
-
`computeNullifier{,Async,Sync}`, `computeOutputsHash{,Async,Sync,FromLimbs}`,
|
|
44
|
-
`computeSwapOutputsHash{,Async,Sync}`. The UTXO-model
|
|
45
|
-
`computeCommitment(utxo)` / `computeNullifier(utxo)` live in
|
|
46
|
-
`core/utxo` and are re-exported as `computeUtxoCommitment` /
|
|
47
|
-
`computeUtxoNullifier`.
|
|
48
|
-
- **OLD types** in `src/core/types.ts` — `CloakNote`, `Transfer`,
|
|
49
|
-
`MaxLengthArray`, `TransferResult`, `DepositResult`, `DepositOptions`,
|
|
50
|
-
`TransferOptions`, `WithdrawOptions`, `DepositStatus`, `SwapOptions`,
|
|
51
|
-
`SwapParams`, `SwapResult`. Plus `proofTimeout` (was unused on the
|
|
52
|
-
snapshot).
|
|
53
|
-
- **`withdraw_regular_final.zkey`** — the ~10 MB legacy circuit asset.
|
|
54
|
-
|
|
55
|
-
### Changed (BREAKING)
|
|
56
|
-
|
|
57
|
-
- **`CloakConfig` is now a snapshot-only type.** It is the structurally
|
|
58
|
-
narrow value returned by `CloakSDK.getConfig()`: exactly seven
|
|
59
|
-
fields — `network`, `cloakKeys`, `programId`, `poolAddress`,
|
|
60
|
-
`merkleTreeAddress`, `treasuryAddress`, `debug`. The constructor
|
|
61
|
-
input is the new exported `CloakSDKOptions` interface (which still
|
|
62
|
-
accepts `keypairBytes`, `wallet`, and `storage` — they are consumed
|
|
63
|
-
into private SDK state and are *never* persisted on the snapshot).
|
|
64
|
-
`sdk.getConfig().keypairBytes` is now a `TS2339` compile-time error.
|
|
65
|
-
- `CloakConfig.cloakKeys` is now typed as `CloakKeyPair | undefined`
|
|
66
|
-
(was `any`).
|
|
67
|
-
- `LocalStorageAdapter` constructor signature is `(keysKey?: string)`;
|
|
68
|
-
the second `notesKey` parameter is gone.
|
|
69
|
-
- `CloakSDK` constructor now throws `CloakError(_, "validation", false)`
|
|
70
|
-
instead of plain `Error` when no signer is provided; `getPublicKey()`
|
|
71
|
-
throws `CloakError(_, "wallet", false)` when neither keypair nor
|
|
72
|
-
wallet adapter is configured. Consumers' `parseError()` /
|
|
73
|
-
`classifyRelayError()` flows now handle these uniformly.
|
|
74
|
-
|
|
75
|
-
### Added
|
|
76
|
-
|
|
77
|
-
- **`LocalStorageAdapter` auto-purges legacy plaintext secrets.** On
|
|
78
|
-
the first construction in any browser origin, the adapter removes the
|
|
79
|
-
pre-0.1.6 `cloak_notes` localStorage entry (which contained `r`
|
|
80
|
-
randomness and `sk_spend` hex in plaintext) and writes a sentinel at
|
|
81
|
-
`cloak_purged_v0_1_5` so subsequent constructions skip the work.
|
|
82
|
-
Closes the upgrade-time security gap without requiring consumers to
|
|
83
|
-
call any helper. Consumers using a non-default legacy key can still
|
|
84
|
-
call `LocalStorageAdapter.purgeLegacyNoteStorage(customKey)`
|
|
85
|
-
explicitly.
|
|
86
|
-
- `LocalStorageAdapter.LEGACY_PURGE_SENTINEL_KEY` and
|
|
87
|
-
`LocalStorageAdapter.LEGACY_NOTES_KEY` static constants for callers
|
|
88
|
-
that want to inspect / reset the purge state.
|
|
89
|
-
- `LocalStorageAdapter.runLegacyPurgeOnce()` static helper for callers
|
|
90
|
-
that want to force the auto-purge before the first adapter
|
|
91
|
-
instantiation (e.g. during an upgrade boot sequence).
|
|
92
|
-
|
|
93
|
-
### Security
|
|
94
|
-
|
|
95
|
-
- `getConfig()` no longer leaks `keypairBytes`. The constructor
|
|
96
|
-
consumed the secret bytes into a `Keypair` and used to spread the
|
|
97
|
-
whole input onto `this.config`; `getConfig()`'s shallow-copy return
|
|
98
|
-
exposed the original `Uint8Array` by reference. `this.config` is now
|
|
99
|
-
built explicitly without `keypairBytes`/`wallet`/`storage`, and the
|
|
100
|
-
type system enforces the asymmetry (see "Changed" above).
|
|
101
|
-
- `LocalStorageAdapter` upgrade-time auto-purge (see "Added") removes
|
|
102
|
-
the now-unredeemable plaintext secrets that pre-0.1.6 versions left
|
|
103
|
-
in browser localStorage.
|
|
104
|
-
|
|
105
|
-
### Migration guide
|
|
106
|
-
|
|
107
|
-
The supported flow for transaction emission is the functional UTXO API.
|
|
108
|
-
|
|
109
|
-
```ts
|
|
110
|
-
import {
|
|
111
|
-
transact,
|
|
112
|
-
fullWithdraw,
|
|
113
|
-
partialWithdraw,
|
|
114
|
-
transfer,
|
|
115
|
-
swapUtxo,
|
|
116
|
-
createUtxo,
|
|
117
|
-
createZeroUtxo,
|
|
118
|
-
generateUtxoKeypair,
|
|
119
|
-
CLOAK_PROGRAM_ID,
|
|
120
|
-
NATIVE_SOL_MINT,
|
|
121
|
-
} from "@cloak.dev/sdk";
|
|
122
|
-
import { Connection, Keypair } from "@solana/web3.js";
|
|
123
|
-
|
|
124
|
-
const connection = new Connection("https://api.mainnet-beta.solana.com");
|
|
125
|
-
const signer = Keypair.fromSecretKey(/* ... */);
|
|
126
|
-
|
|
127
|
-
// Deposit (positive externalAmount).
|
|
128
|
-
const owner = await generateUtxoKeypair();
|
|
129
|
-
const output = await createUtxo(10_000_000n, owner, NATIVE_SOL_MINT);
|
|
130
|
-
const deposited = await transact(
|
|
131
|
-
{
|
|
132
|
-
inputUtxos: [
|
|
133
|
-
await createZeroUtxo(NATIVE_SOL_MINT),
|
|
134
|
-
await createZeroUtxo(NATIVE_SOL_MINT),
|
|
135
|
-
],
|
|
136
|
-
outputUtxos: [output, await createZeroUtxo(NATIVE_SOL_MINT)],
|
|
137
|
-
externalAmount: 10_000_000n,
|
|
138
|
-
depositor: signer.publicKey,
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
connection,
|
|
142
|
-
programId: CLOAK_PROGRAM_ID,
|
|
143
|
-
relayUrl: "https://api.cloak.ag",
|
|
144
|
-
depositorKeypair: signer,
|
|
145
|
-
},
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
// Reclaim to a public address.
|
|
149
|
-
await fullWithdraw(deposited.outputUtxos, signer.publicKey, {
|
|
150
|
-
connection,
|
|
151
|
-
programId: CLOAK_PROGRAM_ID,
|
|
152
|
-
relayUrl: "https://api.cloak.ag",
|
|
153
|
-
depositorKeypair: signer,
|
|
154
|
-
cachedMerkleTree: deposited.merkleTree,
|
|
155
|
-
});
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
The `CloakSDK` class is retained as a thin config + read-only helper
|
|
159
|
-
(`getPublicKey`, `getCurrentRoot`, `getMerkleProof`,
|
|
160
|
-
`getTransactionStatus`, `importWalletKeys`, `exportWalletKeys`,
|
|
161
|
-
`getConfig`). Construction is unchanged for new fields.
|
|
162
|
-
|
|
163
|
-
### Verification
|
|
164
|
-
|
|
165
|
-
- Mainnet end-to-end smoke (deposit + withdraw cycle) on the patched
|
|
166
|
-
SDK: deposit signature
|
|
167
|
-
`5iGmrMYoSucYJ5PzEo5BCWioqTof4KNfjowq14ZYrbQACicaP2tJdJfRUy2CctsQLyNu6xpYXn8gYBg3EefsmXrv`
|
|
168
|
-
(slot 416038846, leaf 318); withdraw signature
|
|
169
|
-
`37A4T1KNiZLkmRvLAtui5ZN2VCmzjsZMzzGNwMosFA2UM1NGn7kP28p5APTvKRb51rNaLmWF42TJqK9GSZM5xCxn`
|
|
170
|
-
(slot 416055377). Net delta on the test wallet matches the expected
|
|
171
|
-
fee schedule (0.005 fixed + 0.3% var).
|
|
172
|
-
|
|
173
|
-
[0.1.6]: https://github.com/cloak-ag/sdk/pull/11
|
package/dist/chunk-2SOX3JNO.js
DELETED
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
-
}) : x)(function(x) {
|
|
4
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
// src/core/utxo.ts
|
|
9
|
-
import { PublicKey } from "@solana/web3.js";
|
|
10
|
-
import { buildPoseidon } from "circomlibjs";
|
|
11
|
-
import { blake3 } from "@noble/hashes/blake3";
|
|
12
|
-
var FIELD_MODULUS = BigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
|
13
|
-
var UTXO_KEY_DOMAIN = new TextEncoder().encode("cloak_utxo_priv_v1");
|
|
14
|
-
var NATIVE_SOL_MINT = new PublicKey("So11111111111111111111111111111111111111112");
|
|
15
|
-
var poseidonInstance = null;
|
|
16
|
-
async function getPoseidon() {
|
|
17
|
-
if (!poseidonInstance) {
|
|
18
|
-
poseidonInstance = await buildPoseidon();
|
|
19
|
-
}
|
|
20
|
-
return poseidonInstance;
|
|
21
|
-
}
|
|
22
|
-
function randomFieldElement() {
|
|
23
|
-
const bytes = new Uint8Array(32);
|
|
24
|
-
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
25
|
-
crypto.getRandomValues(bytes);
|
|
26
|
-
} else {
|
|
27
|
-
const { randomBytes } = __require("crypto");
|
|
28
|
-
const nodeBytes = randomBytes(32);
|
|
29
|
-
for (let i = 0; i < 32; i++) {
|
|
30
|
-
bytes[i] = nodeBytes[i];
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
let value = BigInt(0);
|
|
34
|
-
for (let i = 0; i < 32; i++) {
|
|
35
|
-
value = value << BigInt(8) | BigInt(bytes[i]);
|
|
36
|
-
}
|
|
37
|
-
return value % (FIELD_MODULUS >> BigInt(4));
|
|
38
|
-
}
|
|
39
|
-
async function generateUtxoKeypair() {
|
|
40
|
-
const poseidon = await getPoseidon();
|
|
41
|
-
const privateKey = randomFieldElement();
|
|
42
|
-
const publicKeyHash = poseidon([privateKey, BigInt(0)]);
|
|
43
|
-
const publicKey = poseidon.F.toObject(publicKeyHash);
|
|
44
|
-
return { privateKey, publicKey };
|
|
45
|
-
}
|
|
46
|
-
async function deriveUtxoKeypairFromSpendKey(skSpend) {
|
|
47
|
-
if (!skSpend || !(skSpend instanceof Uint8Array) || skSpend.length !== 32) {
|
|
48
|
-
throw new Error("skSpend must be 32 bytes");
|
|
49
|
-
}
|
|
50
|
-
const preimage = new Uint8Array(UTXO_KEY_DOMAIN.length + skSpend.length);
|
|
51
|
-
preimage.set(UTXO_KEY_DOMAIN, 0);
|
|
52
|
-
preimage.set(skSpend, UTXO_KEY_DOMAIN.length);
|
|
53
|
-
const hash = blake3(preimage);
|
|
54
|
-
let value = BigInt(0);
|
|
55
|
-
for (let i = 0; i < 32; i++) {
|
|
56
|
-
value = value << BigInt(8) | BigInt(hash[i]);
|
|
57
|
-
}
|
|
58
|
-
const privateKey = value % (FIELD_MODULUS >> BigInt(4));
|
|
59
|
-
const publicKey = await derivePublicKey(privateKey);
|
|
60
|
-
return { privateKey, publicKey };
|
|
61
|
-
}
|
|
62
|
-
async function derivePublicKey(privateKey) {
|
|
63
|
-
const poseidon = await getPoseidon();
|
|
64
|
-
const hash = poseidon([privateKey, BigInt(0)]);
|
|
65
|
-
return poseidon.F.toObject(hash);
|
|
66
|
-
}
|
|
67
|
-
async function createUtxo(amount, keypair, mintAddress = NATIVE_SOL_MINT) {
|
|
68
|
-
const blinding = randomFieldElement();
|
|
69
|
-
const utxo = {
|
|
70
|
-
amount,
|
|
71
|
-
keypair,
|
|
72
|
-
blinding,
|
|
73
|
-
mintAddress
|
|
74
|
-
};
|
|
75
|
-
utxo.commitment = await computeCommitment(utxo);
|
|
76
|
-
return utxo;
|
|
77
|
-
}
|
|
78
|
-
async function createZeroUtxo(mintAddress = NATIVE_SOL_MINT, salt) {
|
|
79
|
-
const poseidon = await getPoseidon();
|
|
80
|
-
const effectiveSalt = salt ?? randomFieldElement();
|
|
81
|
-
const publicKeyHash = poseidon([effectiveSalt, BigInt(0)]);
|
|
82
|
-
const publicKey = poseidon.F.toObject(publicKeyHash);
|
|
83
|
-
const keypair = { privateKey: effectiveSalt, publicKey };
|
|
84
|
-
const utxo = {
|
|
85
|
-
amount: BigInt(0),
|
|
86
|
-
keypair,
|
|
87
|
-
blinding: BigInt(0),
|
|
88
|
-
mintAddress
|
|
89
|
-
};
|
|
90
|
-
utxo.commitment = await computeCommitment(utxo);
|
|
91
|
-
utxo.index = 0;
|
|
92
|
-
return utxo;
|
|
93
|
-
}
|
|
94
|
-
function pubkeyToFieldElement(pubkey) {
|
|
95
|
-
const bytes = pubkey.toBytes();
|
|
96
|
-
let value = BigInt(0);
|
|
97
|
-
for (let i = 0; i < 32; i++) {
|
|
98
|
-
value = value << BigInt(8) | BigInt(bytes[i]);
|
|
99
|
-
}
|
|
100
|
-
return value % FIELD_MODULUS;
|
|
101
|
-
}
|
|
102
|
-
async function computeCommitment(utxo) {
|
|
103
|
-
const poseidon = await getPoseidon();
|
|
104
|
-
const mintField = pubkeyToFieldElement(utxo.mintAddress);
|
|
105
|
-
const hash = poseidon([
|
|
106
|
-
utxo.amount,
|
|
107
|
-
utxo.keypair.publicKey,
|
|
108
|
-
utxo.blinding,
|
|
109
|
-
mintField
|
|
110
|
-
]);
|
|
111
|
-
return poseidon.F.toObject(hash);
|
|
112
|
-
}
|
|
113
|
-
async function computeSignature(privateKey, commitment, pathIndex) {
|
|
114
|
-
const poseidon = await getPoseidon();
|
|
115
|
-
const hash = poseidon([privateKey, commitment, pathIndex]);
|
|
116
|
-
return poseidon.F.toObject(hash);
|
|
117
|
-
}
|
|
118
|
-
async function computeNullifier(utxo) {
|
|
119
|
-
if (utxo.index === void 0) {
|
|
120
|
-
throw new Error("UTXO must have an index to compute nullifier");
|
|
121
|
-
}
|
|
122
|
-
const poseidon = await getPoseidon();
|
|
123
|
-
const commitment = utxo.commitment ?? await computeCommitment(utxo);
|
|
124
|
-
const pathIndex = BigInt(utxo.index);
|
|
125
|
-
const signature = await computeSignature(
|
|
126
|
-
utxo.keypair.privateKey,
|
|
127
|
-
commitment,
|
|
128
|
-
pathIndex
|
|
129
|
-
);
|
|
130
|
-
const hash = poseidon([commitment, pathIndex, signature]);
|
|
131
|
-
return poseidon.F.toObject(hash);
|
|
132
|
-
}
|
|
133
|
-
function serializeUtxo(utxo) {
|
|
134
|
-
const buffer = new ArrayBuffer(128);
|
|
135
|
-
const view = new DataView(buffer);
|
|
136
|
-
const amountBytes = bigintToBytes(utxo.amount, 8);
|
|
137
|
-
for (let i = 0; i < 8; i++) {
|
|
138
|
-
view.setUint8(i, amountBytes[i]);
|
|
139
|
-
}
|
|
140
|
-
const privkeyBytes = bigintToBytes(utxo.keypair.privateKey, 32);
|
|
141
|
-
for (let i = 0; i < 32; i++) {
|
|
142
|
-
view.setUint8(8 + i, privkeyBytes[i]);
|
|
143
|
-
}
|
|
144
|
-
const blindingBytes = bigintToBytes(utxo.blinding, 32);
|
|
145
|
-
for (let i = 0; i < 32; i++) {
|
|
146
|
-
view.setUint8(40 + i, blindingBytes[i]);
|
|
147
|
-
}
|
|
148
|
-
const mintBytes = utxo.mintAddress.toBytes();
|
|
149
|
-
for (let i = 0; i < 32; i++) {
|
|
150
|
-
view.setUint8(72 + i, mintBytes[i]);
|
|
151
|
-
}
|
|
152
|
-
view.setUint32(104, utxo.index ?? 0, true);
|
|
153
|
-
return new Uint8Array(buffer);
|
|
154
|
-
}
|
|
155
|
-
async function deserializeUtxo(bytes) {
|
|
156
|
-
const view = new DataView(bytes.buffer, bytes.byteOffset);
|
|
157
|
-
const amountBytes = bytes.slice(0, 8);
|
|
158
|
-
const amount = bytesToBigint(amountBytes);
|
|
159
|
-
const privkeyBytes = bytes.slice(8, 40);
|
|
160
|
-
const privateKey = bytesToBigint(privkeyBytes);
|
|
161
|
-
const publicKey = await derivePublicKey(privateKey);
|
|
162
|
-
const blindingBytes = bytes.slice(40, 72);
|
|
163
|
-
const blinding = bytesToBigint(blindingBytes);
|
|
164
|
-
const mintBytes = bytes.slice(72, 104);
|
|
165
|
-
const mintAddress = new PublicKey(mintBytes);
|
|
166
|
-
const index = view.getUint32(104, true);
|
|
167
|
-
const utxo = {
|
|
168
|
-
amount,
|
|
169
|
-
keypair: { privateKey, publicKey },
|
|
170
|
-
blinding,
|
|
171
|
-
mintAddress,
|
|
172
|
-
index: index > 0 ? index : void 0
|
|
173
|
-
};
|
|
174
|
-
utxo.commitment = await computeCommitment(utxo);
|
|
175
|
-
return utxo;
|
|
176
|
-
}
|
|
177
|
-
function bigintToBytes(value, length) {
|
|
178
|
-
const result = new Uint8Array(length);
|
|
179
|
-
let remaining = value;
|
|
180
|
-
for (let i = 0; i < length; i++) {
|
|
181
|
-
result[i] = Number(remaining & BigInt(255));
|
|
182
|
-
remaining >>= BigInt(8);
|
|
183
|
-
}
|
|
184
|
-
return result;
|
|
185
|
-
}
|
|
186
|
-
function bytesToBigint(bytes) {
|
|
187
|
-
let result = BigInt(0);
|
|
188
|
-
for (let i = bytes.length - 1; i >= 0; i--) {
|
|
189
|
-
result = result << BigInt(8) | BigInt(bytes[i]);
|
|
190
|
-
}
|
|
191
|
-
return result;
|
|
192
|
-
}
|
|
193
|
-
function bigintToHex(value) {
|
|
194
|
-
return value.toString(16).padStart(64, "0");
|
|
195
|
-
}
|
|
196
|
-
function hexToBigint(hex) {
|
|
197
|
-
const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
198
|
-
return BigInt("0x" + cleanHex);
|
|
199
|
-
}
|
|
200
|
-
function bigintToBytes32(value) {
|
|
201
|
-
const result = new Uint8Array(32);
|
|
202
|
-
let remaining = value;
|
|
203
|
-
for (let i = 31; i >= 0; i--) {
|
|
204
|
-
result[i] = Number(remaining & BigInt(255));
|
|
205
|
-
remaining >>= BigInt(8);
|
|
206
|
-
}
|
|
207
|
-
return result;
|
|
208
|
-
}
|
|
209
|
-
async function utxoEquals(a, b) {
|
|
210
|
-
const commitmentA = a.commitment ?? await computeCommitment(a);
|
|
211
|
-
const commitmentB = b.commitment ?? await computeCommitment(b);
|
|
212
|
-
return commitmentA === commitmentB;
|
|
213
|
-
}
|
|
214
|
-
function sumUtxoAmounts(utxos) {
|
|
215
|
-
return utxos.reduce((sum, utxo) => sum + utxo.amount, BigInt(0));
|
|
216
|
-
}
|
|
217
|
-
function selectUtxos(available, targetAmount) {
|
|
218
|
-
const sorted = [...available].sort(
|
|
219
|
-
(a, b) => Number(b.amount - a.amount)
|
|
220
|
-
);
|
|
221
|
-
const selected = [];
|
|
222
|
-
let total = BigInt(0);
|
|
223
|
-
for (const utxo of sorted) {
|
|
224
|
-
if (total >= targetAmount) break;
|
|
225
|
-
selected.push(utxo);
|
|
226
|
-
total += utxo.amount;
|
|
227
|
-
}
|
|
228
|
-
if (total < targetAmount) {
|
|
229
|
-
return null;
|
|
230
|
-
}
|
|
231
|
-
return selected;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
export {
|
|
235
|
-
__require,
|
|
236
|
-
NATIVE_SOL_MINT,
|
|
237
|
-
randomFieldElement,
|
|
238
|
-
generateUtxoKeypair,
|
|
239
|
-
deriveUtxoKeypairFromSpendKey,
|
|
240
|
-
derivePublicKey,
|
|
241
|
-
createUtxo,
|
|
242
|
-
createZeroUtxo,
|
|
243
|
-
pubkeyToFieldElement,
|
|
244
|
-
computeCommitment,
|
|
245
|
-
computeSignature,
|
|
246
|
-
computeNullifier,
|
|
247
|
-
serializeUtxo,
|
|
248
|
-
deserializeUtxo,
|
|
249
|
-
bigintToHex,
|
|
250
|
-
hexToBigint,
|
|
251
|
-
bigintToBytes32,
|
|
252
|
-
utxoEquals,
|
|
253
|
-
sumUtxoAmounts,
|
|
254
|
-
selectUtxos
|
|
255
|
-
};
|