@veilo/sdk-core 0.3.3 → 0.4.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 +42 -3
- package/SHIELD_INTEGRATION.md +143 -0
- package/config.d.ts +2 -0
- package/config.js +4 -0
- package/dist/cjs/client.d.ts +5 -5
- package/dist/cjs/client.js +79 -42
- package/dist/cjs/compactNote.d.ts +107 -0
- package/dist/cjs/compactNote.js +167 -0
- package/dist/cjs/events.d.ts +1 -1
- package/dist/cjs/events.js +2 -2
- package/dist/cjs/idl/privacy_pool.d.ts +5 -0
- package/dist/cjs/idl/privacy_pool.js +15218 -0
- package/dist/cjs/index.d.ts +16 -13
- package/dist/cjs/index.js +40 -30
- package/dist/cjs/merkle.d.ts +13 -0
- package/dist/cjs/merkle.js +31 -8
- package/dist/cjs/program.d.ts +11 -0
- package/dist/cjs/program.js +26 -3
- package/dist/cjs/proof.d.ts +1 -1
- package/dist/cjs/proof.js +37 -37
- package/dist/cjs/prover.d.ts +1 -1
- package/dist/cjs/prover.js +2 -2
- package/dist/cjs/random.d.ts +16 -0
- package/dist/cjs/random.js +28 -0
- package/dist/cjs/relayer.d.ts +27 -4
- package/dist/cjs/relayer.js +16 -5
- package/dist/cjs/shield/alt.d.ts +87 -0
- package/dist/cjs/shield/alt.js +194 -0
- package/dist/cjs/shield/computeBudget.d.ts +61 -0
- package/dist/cjs/shield/computeBudget.js +64 -0
- package/dist/cjs/shield/errors.d.ts +58 -0
- package/dist/cjs/shield/errors.js +121 -0
- package/dist/cjs/shield/finalize.d.ts +45 -0
- package/dist/cjs/shield/finalize.js +119 -0
- package/dist/cjs/shield/index.d.ts +35 -0
- package/dist/cjs/shield/index.js +68 -0
- package/dist/cjs/shield/ix.d.ts +54 -0
- package/dist/cjs/shield/ix.js +119 -0
- package/dist/cjs/shield/owner.d.ts +36 -0
- package/dist/cjs/shield/owner.js +126 -0
- package/dist/cjs/shield/ports.d.ts +43 -0
- package/dist/cjs/shield/ports.js +153 -0
- package/dist/cjs/shield/preflight.d.ts +30 -0
- package/dist/cjs/shield/preflight.js +154 -0
- package/dist/cjs/shield/shield.d.ts +68 -0
- package/dist/cjs/shield/shield.js +499 -0
- package/dist/cjs/shield/types.d.ts +202 -0
- package/dist/cjs/shield/types.js +2 -0
- package/dist/cjs/utxo.d.ts +20 -0
- package/dist/cjs/utxo.js +41 -28
- package/dist/esm/client.d.ts +407 -0
- package/dist/esm/client.js +9 -5
- package/dist/esm/compactNote.d.ts +107 -0
- package/dist/esm/compactNote.js +156 -0
- package/dist/esm/config.d.ts +82 -0
- package/dist/esm/events.d.ts +77 -0
- package/dist/esm/events.js +1 -1
- package/dist/esm/idl/privacy_pool.d.ts +5 -0
- package/dist/esm/idl/privacy_pool.js +15216 -0
- package/dist/esm/index.d.ts +16 -0
- package/dist/esm/index.js +18 -11
- package/dist/esm/merkle.d.ts +77 -0
- package/dist/esm/merkle.js +23 -1
- package/dist/esm/package.json +1 -0
- package/dist/esm/poseidon.d.ts +29 -0
- package/dist/esm/program.d.ts +37 -0
- package/dist/esm/program.js +23 -1
- package/dist/esm/proof.d.ts +183 -0
- package/dist/esm/proof.js +2 -2
- package/dist/esm/prover.d.ts +54 -0
- package/dist/esm/prover.js +1 -1
- package/dist/esm/random.d.ts +16 -0
- package/dist/esm/random.js +21 -0
- package/dist/esm/relayer.d.ts +318 -0
- package/dist/esm/relayer.js +16 -5
- package/dist/esm/retry.d.ts +32 -0
- package/dist/esm/shield/alt.d.ts +87 -0
- package/dist/esm/shield/alt.js +186 -0
- package/dist/esm/shield/computeBudget.d.ts +61 -0
- package/dist/esm/shield/computeBudget.js +61 -0
- package/dist/esm/shield/errors.d.ts +58 -0
- package/dist/esm/shield/errors.js +115 -0
- package/dist/esm/shield/finalize.d.ts +45 -0
- package/dist/esm/shield/finalize.js +83 -0
- package/dist/esm/shield/index.d.ts +35 -0
- package/dist/esm/shield/index.js +32 -0
- package/dist/esm/shield/ix.d.ts +54 -0
- package/dist/esm/shield/ix.js +82 -0
- package/dist/esm/shield/owner.d.ts +36 -0
- package/dist/esm/shield/owner.js +122 -0
- package/dist/esm/shield/ports.d.ts +43 -0
- package/dist/esm/shield/ports.js +147 -0
- package/dist/esm/shield/preflight.d.ts +30 -0
- package/dist/esm/shield/preflight.js +151 -0
- package/dist/esm/shield/shield.d.ts +68 -0
- package/dist/esm/shield/shield.js +492 -0
- package/dist/esm/shield/types.d.ts +202 -0
- package/dist/esm/shield/types.js +1 -0
- package/dist/esm/utxo.d.ts +235 -0
- package/dist/esm/utxo.js +18 -8
- package/dist/src/client.d.ts +407 -0
- package/dist/src/client.js +951 -0
- package/dist/src/compactNote.d.ts +107 -0
- package/dist/src/compactNote.js +167 -0
- package/dist/src/config.d.ts +82 -0
- package/dist/src/config.js +57 -0
- package/dist/src/events.d.ts +77 -0
- package/dist/src/events.js +167 -0
- package/dist/src/idl/privacy_pool.d.ts +5 -0
- package/dist/src/idl/privacy_pool.js +15218 -0
- package/dist/src/index.d.ts +16 -0
- package/dist/src/index.js +67 -0
- package/dist/src/merkle.d.ts +77 -0
- package/dist/src/merkle.js +156 -0
- package/dist/src/poseidon.d.ts +29 -0
- package/dist/src/poseidon.js +100 -0
- package/dist/src/program.d.ts +37 -0
- package/dist/src/program.js +61 -0
- package/dist/src/proof.d.ts +183 -0
- package/dist/src/proof.js +292 -0
- package/dist/src/prover.d.ts +54 -0
- package/dist/src/prover.js +112 -0
- package/dist/src/random.d.ts +16 -0
- package/dist/src/random.js +28 -0
- package/dist/src/relayer.d.ts +318 -0
- package/dist/src/relayer.js +257 -0
- package/dist/src/retry.d.ts +32 -0
- package/dist/src/retry.js +75 -0
- package/dist/src/shield/alt.d.ts +87 -0
- package/dist/src/shield/alt.js +194 -0
- package/dist/src/shield/computeBudget.d.ts +61 -0
- package/dist/src/shield/computeBudget.js +64 -0
- package/dist/src/shield/errors.d.ts +58 -0
- package/dist/src/shield/errors.js +121 -0
- package/dist/src/shield/finalize.d.ts +45 -0
- package/dist/src/shield/finalize.js +119 -0
- package/dist/src/shield/index.d.ts +35 -0
- package/dist/src/shield/index.js +68 -0
- package/dist/src/shield/ix.d.ts +54 -0
- package/dist/src/shield/ix.js +119 -0
- package/dist/src/shield/owner.d.ts +36 -0
- package/dist/src/shield/owner.js +126 -0
- package/dist/src/shield/ports.d.ts +43 -0
- package/dist/src/shield/ports.js +153 -0
- package/dist/src/shield/preflight.d.ts +30 -0
- package/dist/src/shield/preflight.js +154 -0
- package/dist/src/shield/shield.d.ts +68 -0
- package/dist/src/shield/shield.js +499 -0
- package/dist/src/shield/types.d.ts +202 -0
- package/dist/src/shield/types.js +2 -0
- package/dist/src/utxo.d.ts +235 -0
- package/dist/src/utxo.js +407 -0
- package/dist/tests/compact-note.test.d.ts +1 -0
- package/dist/tests/compact-note.test.js +173 -0
- package/dist/tests/config.test.d.ts +1 -0
- package/dist/tests/config.test.js +102 -0
- package/dist/tests/edge-cases.test.d.ts +1 -0
- package/dist/tests/edge-cases.test.js +220 -0
- package/dist/tests/encryption.test.d.ts +1 -0
- package/dist/tests/encryption.test.js +215 -0
- package/dist/tests/events.test.d.ts +1 -0
- package/dist/tests/events.test.js +78 -0
- package/dist/tests/multi-tree.test.d.ts +1 -0
- package/dist/tests/multi-tree.test.js +405 -0
- package/dist/tests/pda.test.d.ts +1 -0
- package/dist/tests/pda.test.js +229 -0
- package/dist/tests/poseidon-builder-parity.test.d.ts +1 -0
- package/dist/tests/poseidon-builder-parity.test.js +72 -0
- package/dist/tests/poseidon.test.d.ts +1 -0
- package/dist/tests/poseidon.test.js +142 -0
- package/dist/tests/proof.test.d.ts +1 -0
- package/dist/tests/proof.test.js +296 -0
- package/dist/tests/relayer.test.d.ts +1 -0
- package/dist/tests/relayer.test.js +271 -0
- package/dist/tests/sdk.integration.test.d.ts +1 -0
- package/dist/tests/sdk.integration.test.js +330 -0
- package/dist/tests/shield-owner.test.d.ts +1 -0
- package/dist/tests/shield-owner.test.js +89 -0
- package/dist/tests/shield-preflight.test.d.ts +1 -0
- package/dist/tests/shield-preflight.test.js +87 -0
- package/dist/tests/shield-realproof.test.d.ts +1 -0
- package/dist/tests/shield-realproof.test.js +272 -0
- package/dist/tests/shield.test.d.ts +1 -0
- package/dist/tests/shield.test.js +403 -0
- package/dist/tests/utxo.test.d.ts +1 -0
- package/dist/tests/utxo.test.js +140 -0
- package/package.json +69 -11
- package/poseidon.d.ts +2 -0
- package/poseidon.js +4 -0
- package/proof.d.ts +2 -0
- package/proof.js +4 -0
- package/prover.d.ts +2 -0
- package/prover.js +4 -0
- package/shield.d.ts +2 -0
- package/shield.js +4 -0
package/README.md
CHANGED
|
@@ -410,13 +410,49 @@ const signature: string = await transact({
|
|
|
410
410
|
publicAmount: 0n,
|
|
411
411
|
inputNullifiers: [nullifier1, nullifier2],
|
|
412
412
|
outputCommitments: [output1.commitment, output2.commitment],
|
|
413
|
-
extData: { recipient, relayer: relayerPubkey, fee: 0n, refund: 0n },
|
|
413
|
+
extData: { recipient, relayer: relayerPubkey, fee: 0n, refund: 0n, claimant },
|
|
414
414
|
proof,
|
|
415
415
|
});
|
|
416
416
|
```
|
|
417
417
|
|
|
418
418
|
---
|
|
419
419
|
|
|
420
|
+
#### Wallet-agnostic shielding
|
|
421
|
+
|
|
422
|
+
`shield()` builds an unsigned v0 deposit transaction for any Solana wallet to
|
|
423
|
+
sign. The signer funds the deposit, while the resulting private note can belong
|
|
424
|
+
to a different Veilo user. The SDK never receives a wallet secret key and never
|
|
425
|
+
broadcasts on the caller's behalf.
|
|
426
|
+
|
|
427
|
+
```ts
|
|
428
|
+
import {
|
|
429
|
+
createTransactionProver,
|
|
430
|
+
resolveShieldOwner,
|
|
431
|
+
shield,
|
|
432
|
+
} from "@veilo/sdk-core";
|
|
433
|
+
|
|
434
|
+
const owner = await resolveShieldOwner({ username: "alice" });
|
|
435
|
+
const result = await shield({
|
|
436
|
+
connection,
|
|
437
|
+
amount: 5_000_000n,
|
|
438
|
+
mint: USDC_MINT,
|
|
439
|
+
owner,
|
|
440
|
+
signer: { publicKey: wallet.publicKey },
|
|
441
|
+
prover: createTransactionProver({ wasmPath, zkeyPath }),
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
const signed = await wallet.signTransaction(result.transaction);
|
|
445
|
+
const signature = await connection.sendRawTransaction(signed.serialize());
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
Use `rebuild()` if the blockhash or deadline expires, and call
|
|
449
|
+
`finalizeShield()` after confirmation to obtain the authoritative Merkle leaf
|
|
450
|
+
index. See [SHIELD_INTEGRATION.md](./SHIELD_INTEGRATION.md) for the complete
|
|
451
|
+
build → sign → submit → finalize flow, retry rules, owner-key invariant, and
|
|
452
|
+
fee/rent requirements.
|
|
453
|
+
|
|
454
|
+
---
|
|
455
|
+
|
|
420
456
|
### 4.8 Private Swaps
|
|
421
457
|
|
|
422
458
|
Cross-pool private swaps via Jupiter/Raydium. For native SOL source pools the SDK automatically composes the required `fund_native_source` + `transact_swap` instructions into a single atomic transaction.
|
|
@@ -452,7 +488,7 @@ const signature: string = await transactSwap({
|
|
|
452
488
|
},
|
|
453
489
|
swapAmount: 1_000_000_000n,
|
|
454
490
|
swapData: jupiterInstructionBytes, // Buffer
|
|
455
|
-
extData: { recipient, relayer: relayerPubkey, fee, refund: 0n },
|
|
491
|
+
extData: { recipient, relayer: relayerPubkey, fee, refund: 0n, claimant },
|
|
456
492
|
sourceVaultTokenAccount,
|
|
457
493
|
sourceMintAccount,
|
|
458
494
|
destVaultTokenAccount,
|
|
@@ -542,7 +578,7 @@ import {
|
|
|
542
578
|
const circuitInputs: TransactionCircuitInputs = prepareTransactionInputs({
|
|
543
579
|
root: tree.root(),
|
|
544
580
|
publicAmount: 1_000_000_000n,
|
|
545
|
-
extData: { recipient, relayer: relayerPubkey, fee: 0n, refund: 0n },
|
|
581
|
+
extData: { recipient, relayer: relayerPubkey, fee: 0n, refund: 0n, claimant },
|
|
546
582
|
mintAddress: NATIVE_SOL_MINT,
|
|
547
583
|
inputs: [input1, input2],
|
|
548
584
|
outputs: [output1, output2],
|
|
@@ -563,6 +599,7 @@ const extDataHash = computeExtDataHash({
|
|
|
563
599
|
relayer,
|
|
564
600
|
fee: 0n,
|
|
565
601
|
refund: 0n,
|
|
602
|
+
claimant,
|
|
566
603
|
});
|
|
567
604
|
|
|
568
605
|
// Compute swap param/data hashes (for swap circuit inputs)
|
|
@@ -1182,6 +1219,7 @@ await transact({
|
|
|
1182
1219
|
relayer: relayerPubkey,
|
|
1183
1220
|
fee: 0n,
|
|
1184
1221
|
refund: 0n,
|
|
1222
|
+
claimant: claimantPubkey,
|
|
1185
1223
|
},
|
|
1186
1224
|
proof,
|
|
1187
1225
|
});
|
|
@@ -1213,6 +1251,7 @@ const circuitInputs: TransactionCircuitInputs = prepareTransactionInputs({
|
|
|
1213
1251
|
relayer: relayerPubkey,
|
|
1214
1252
|
fee: 0n,
|
|
1215
1253
|
refund: 0n,
|
|
1254
|
+
claimant: claimantPubkey,
|
|
1216
1255
|
},
|
|
1217
1256
|
mintAddress: NATIVE_SOL_MINT,
|
|
1218
1257
|
inputs: [input1, input2],
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Wallet-agnostic shielding
|
|
2
|
+
|
|
3
|
+
`shield()` builds an unsigned Solana v0 transaction that moves funds from any
|
|
4
|
+
signing wallet into Veilo's privacy pool. The wallet that pays and the Veilo
|
|
5
|
+
account that owns the resulting private note are independent.
|
|
6
|
+
|
|
7
|
+
This is a build API, like Jupiter's transaction APIs. The SDK proves, encrypts,
|
|
8
|
+
and assembles. Your application asks its wallet to sign, submits the signed
|
|
9
|
+
bytes, confirms them, and handles retry.
|
|
10
|
+
|
|
11
|
+
## Security model
|
|
12
|
+
|
|
13
|
+
- `signer.publicKey` is the transaction fee payer and public funding source.
|
|
14
|
+
- `owner.veiloPublicKey` controls spending the private note.
|
|
15
|
+
- `owner.noteViewingKey` controls detecting and decrypting the note during a
|
|
16
|
+
chain scan.
|
|
17
|
+
- The SDK never receives a signer secret key.
|
|
18
|
+
- The compact note cipher is embedded in the on-chain instruction, so note
|
|
19
|
+
recovery does not depend on a webhook or a later relayer callback.
|
|
20
|
+
|
|
21
|
+
The two owner keys must belong to the same Veilo account. If they do not, the
|
|
22
|
+
note is spendable but invisible to its owner. Always use `resolveShieldOwner()`;
|
|
23
|
+
construct an owner manually only when you can prove the pairing yourself.
|
|
24
|
+
|
|
25
|
+
## Complete flow
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { Connection, PublicKey } from "@solana/web3.js";
|
|
29
|
+
import {
|
|
30
|
+
createTransactionProver,
|
|
31
|
+
finalizeShield,
|
|
32
|
+
mapShieldError,
|
|
33
|
+
rebuild,
|
|
34
|
+
resolveShieldOwner,
|
|
35
|
+
shield,
|
|
36
|
+
} from "@veilo/sdk-core";
|
|
37
|
+
|
|
38
|
+
const connection = new Connection(RPC_URL, "confirmed");
|
|
39
|
+
const owner = await resolveShieldOwner({ username: "alice" });
|
|
40
|
+
const prover = createTransactionProver({
|
|
41
|
+
wasmPath: TRANSACTION_WASM_URL,
|
|
42
|
+
zkeyPath: TRANSACTION_ZKEY_URL,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
let result = await shield({
|
|
46
|
+
connection,
|
|
47
|
+
amount: 5_000_000n, // base units
|
|
48
|
+
mint: new PublicKey(USDC_MINT),
|
|
49
|
+
owner,
|
|
50
|
+
signer: { publicKey: wallet.publicKey },
|
|
51
|
+
prover,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
async function signAndSubmit() {
|
|
55
|
+
const signed = await wallet.signTransaction(result.transaction);
|
|
56
|
+
const signature = await connection.sendRawTransaction(signed.serialize(), {
|
|
57
|
+
maxRetries: 0,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const confirmation = await connection.confirmTransaction(
|
|
61
|
+
{
|
|
62
|
+
signature,
|
|
63
|
+
blockhash: result.blockhash,
|
|
64
|
+
lastValidBlockHeight: result.lastValidBlockHeight,
|
|
65
|
+
},
|
|
66
|
+
"confirmed",
|
|
67
|
+
);
|
|
68
|
+
if (confirmation.value.err) throw confirmation.value.err;
|
|
69
|
+
return signature;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let signature: string;
|
|
73
|
+
try {
|
|
74
|
+
signature = await signAndSubmit();
|
|
75
|
+
} catch (cause) {
|
|
76
|
+
const error = mapShieldError(cause);
|
|
77
|
+
if (error.retryable !== "rebuild") throw error;
|
|
78
|
+
|
|
79
|
+
// Reuses the proof and note; only the blockhash and deadline are refreshed.
|
|
80
|
+
result = await rebuild(result, { connection });
|
|
81
|
+
signature = await signAndSubmit(); // the rebuilt transaction needs a new signature
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const finalized = await finalizeShield({
|
|
85
|
+
connection,
|
|
86
|
+
signature,
|
|
87
|
+
note: result.note,
|
|
88
|
+
owner,
|
|
89
|
+
commitment: "finalized",
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
console.log(finalized.leafIndex, finalized.treeId);
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`predictedLeafIndex` is informational only. Concurrent deposits can move the
|
|
96
|
+
actual index before your transaction lands. Persist `finalized.leafIndex`, which
|
|
97
|
+
comes from this transaction's `CommitmentEvent`.
|
|
98
|
+
|
|
99
|
+
## Preflight and wallet UX
|
|
100
|
+
|
|
101
|
+
Preflight runs before the 10–30 second proof and checks pool limits, token
|
|
102
|
+
ownership and balance, ATA state, and the SOL needed by the transaction. If an
|
|
103
|
+
SPL token account has any delegate—even with a delegated amount of zero—the
|
|
104
|
+
program rejects it. `TOKEN_ACCOUNT_DELEGATED` includes a `remedy` with the
|
|
105
|
+
`Revoke` instruction the wallet can sign before retrying.
|
|
106
|
+
|
|
107
|
+
The wallet may describe the Veilo instruction as an unknown program. Show the
|
|
108
|
+
amount and destination in your own review screen before opening the wallet so
|
|
109
|
+
the signer can verify what it is authorizing.
|
|
110
|
+
|
|
111
|
+
## SOL requirements
|
|
112
|
+
|
|
113
|
+
The signer pays:
|
|
114
|
+
|
|
115
|
+
- the amount itself for native SOL shields;
|
|
116
|
+
- the normal transaction and priority fees; and
|
|
117
|
+
- rent for two nullifier-marker accounts (about 0.00192 SOL at current rent).
|
|
118
|
+
|
|
119
|
+
The program fixes the marker payer to the signer. A zero-SOL wallet therefore
|
|
120
|
+
cannot use this path, even for an SPL token shield and even if another service
|
|
121
|
+
would sponsor the transaction fee. This API does not provide a gasless mode.
|
|
122
|
+
|
|
123
|
+
## Retry decisions
|
|
124
|
+
|
|
125
|
+
Use `mapShieldError()` and branch on `retryable`:
|
|
126
|
+
|
|
127
|
+
| Error | Retry class | Action |
|
|
128
|
+
|---|---|---|
|
|
129
|
+
| `BLOCKHASH_EXPIRED`, `DEADLINE_EXPIRED` | `rebuild` | Call `rebuild()`, sign again, submit again. |
|
|
130
|
+
| `ROOT_STALE` | `reshield` | Call `shield()` again; a fresh proof is required. |
|
|
131
|
+
| `NULLIFIER_COLLISION` | `none` | Treat as already submitted and check the original signature. |
|
|
132
|
+
| Preflight and configuration errors | `none` | Correct the stated condition before retrying. |
|
|
133
|
+
|
|
134
|
+
`ShieldProofCache` is public and JSON-serializable. `ShieldNote` is secret. Do
|
|
135
|
+
not store or transmit the note in the clear, and do not bundle it with the proof
|
|
136
|
+
cache merely because the cache is safe to move between processes.
|
|
137
|
+
|
|
138
|
+
## Optional mailbox delivery
|
|
139
|
+
|
|
140
|
+
`finalizeShield()` returns an encrypted `blob` suitable for `/notes/save`.
|
|
141
|
+
Supplying `recipientWalletPublicKey` gives faster mailbox delivery and push
|
|
142
|
+
notifications, but records a sender-to-recipient association at the relayer.
|
|
143
|
+
Omitting it keeps discovery chain-only and preserves that privacy boundary.
|
package/config.d.ts
ADDED
package/config.js
ADDED
package/dist/cjs/client.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as anchor from "@coral-xyz/anchor";
|
|
2
|
-
import { Program, Idl } from "@coral-xyz/anchor";
|
|
2
|
+
import type { Program, Idl } from "@coral-xyz/anchor";
|
|
3
3
|
import { PublicKey, Transaction, Keypair } from "@solana/web3.js";
|
|
4
|
-
import { TransactionProofStruct, TransactionProofBuilder, ExtData } from "./proof";
|
|
5
|
-
import { SerializedUTXO, InputUTXO } from "./utxo";
|
|
6
|
-
import { MerkleTree } from "./merkle";
|
|
7
|
-
import { PrivacyConfigAccount } from "./config";
|
|
4
|
+
import { TransactionProofStruct, TransactionProofBuilder, ExtData } from "./proof.js";
|
|
5
|
+
import { SerializedUTXO, InputUTXO } from "./utxo.js";
|
|
6
|
+
import { MerkleTree } from "./merkle.js";
|
|
7
|
+
import { PrivacyConfigAccount } from "./config.js";
|
|
8
8
|
/**
|
|
9
9
|
* Get pool PDAs for a given mint address.
|
|
10
10
|
* Note: All seeds use "v3" suffix.
|
package/dist/cjs/client.js
CHANGED
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.getPoolPdas = getPoolPdas;
|
|
4
37
|
exports.getNoteTreePda = getNoteTreePda;
|
|
@@ -25,13 +58,17 @@ exports.updateGlobalConfig = updateGlobalConfig;
|
|
|
25
58
|
exports.getSwapExecutorPda = getSwapExecutorPda;
|
|
26
59
|
exports.fundNativeSource = fundNativeSource;
|
|
27
60
|
exports.transactSwap = transactSwap;
|
|
28
|
-
const
|
|
61
|
+
const anchor = __importStar(require("@coral-xyz/anchor"));
|
|
62
|
+
// Pulled off the namespace rather than named-imported. @coral-xyz/anchor is CJS
|
|
63
|
+
// with no `exports` map, and Node ESM's named-export detection cannot see `BN`
|
|
64
|
+
// (it is re-exported from bn.js), so `import { BN }` throws at runtime under ESM.
|
|
65
|
+
const { BN } = anchor;
|
|
29
66
|
const web3_js_1 = require("@solana/web3.js");
|
|
30
67
|
const spl_token_1 = require("@solana/spl-token");
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
const
|
|
68
|
+
const proof_js_1 = require("./proof.js");
|
|
69
|
+
const utxo_js_1 = require("./utxo.js");
|
|
70
|
+
const config_js_1 = require("./config.js");
|
|
71
|
+
const retry_js_1 = require("./retry.js");
|
|
35
72
|
// -----------------------------------------------------------------------------
|
|
36
73
|
// PDA Helpers
|
|
37
74
|
// -----------------------------------------------------------------------------
|
|
@@ -282,7 +319,7 @@ async function initializePool(params) {
|
|
|
282
319
|
const { config, vault, nullifiers } = getPoolPdas(program.programId, mintAddress);
|
|
283
320
|
const noteTree = getNoteTreePda(program.programId, mintAddress, 0);
|
|
284
321
|
await program.methods
|
|
285
|
-
.initialize(feeBps, mintAddress, minDepositAmount ? new
|
|
322
|
+
.initialize(feeBps, mintAddress, minDepositAmount ? new BN(minDepositAmount.toString()) : null, maxDepositAmount ? new BN(maxDepositAmount.toString()) : null, minWithdrawAmount ? new BN(minWithdrawAmount.toString()) : null, maxWithdrawAmount ? new BN(maxWithdrawAmount.toString()) : null)
|
|
286
323
|
.accounts({
|
|
287
324
|
config,
|
|
288
325
|
vault,
|
|
@@ -305,7 +342,7 @@ async function updatePoolConfig(params) {
|
|
|
305
342
|
const { program, admin, mintAddress, minDepositAmount, maxDepositAmount, minWithdrawAmount, maxWithdrawAmount, feeBps, feeErrorMarginBps, minWithdrawalFee, minSwapFee, swapFeeBps, } = params;
|
|
306
343
|
const { config } = getPoolPdas(program.programId, mintAddress);
|
|
307
344
|
await program.methods
|
|
308
|
-
.updatePoolConfig(mintAddress, minDepositAmount ? new
|
|
345
|
+
.updatePoolConfig(mintAddress, minDepositAmount ? new BN(minDepositAmount.toString()) : null, maxDepositAmount ? new BN(maxDepositAmount.toString()) : null, minWithdrawAmount ? new BN(minWithdrawAmount.toString()) : null, maxWithdrawAmount ? new BN(maxWithdrawAmount.toString()) : null, feeBps != null ? new BN(feeBps) : null, minWithdrawalFee ? new BN(minWithdrawalFee.toString()) : null, feeErrorMarginBps != null ? new BN(feeErrorMarginBps) : null, minSwapFee ? new BN(minSwapFee.toString()) : null, swapFeeBps != null ? new BN(swapFeeBps) : null)
|
|
309
346
|
.accounts({
|
|
310
347
|
config,
|
|
311
348
|
admin: admin.publicKey,
|
|
@@ -412,8 +449,8 @@ async function transact(params) {
|
|
|
412
449
|
// Nullifier markers are global (no tree_id) to prevent cross-tree double-spend
|
|
413
450
|
const nullifierMarker0 = getNullifierMarkerPda(program.programId, mintAddress, inputNullifiers[0]);
|
|
414
451
|
const nullifierMarker1 = getNullifierMarkerPda(program.programId, mintAddress, inputNullifiers[1]);
|
|
415
|
-
const extDataHash = (0,
|
|
416
|
-
const isNativeSol = mintAddress.equals(
|
|
452
|
+
const extDataHash = (0, proof_js_1.computeExtDataHash)(extData);
|
|
453
|
+
const isNativeSol = mintAddress.equals(config_js_1.NATIVE_SOL_MINT);
|
|
417
454
|
// For SPL tokens, resolve token accounts
|
|
418
455
|
let vaultTA = vaultTokenAccount;
|
|
419
456
|
let userTA = userTokenAccount;
|
|
@@ -434,11 +471,11 @@ async function transact(params) {
|
|
|
434
471
|
}
|
|
435
472
|
}
|
|
436
473
|
const tx = program.methods
|
|
437
|
-
.transact(Array.from(root), inputTreeId, outputTreeId, new
|
|
474
|
+
.transact(Array.from(root), inputTreeId, outputTreeId, new BN(publicAmount.toString()), Array.from(extDataHash), mintAddress, Array.from(inputNullifiers[0]), Array.from(inputNullifiers[1]), Array.from(outputCommitments[0]), Array.from(outputCommitments[1]), new BN(deadline.toString()), {
|
|
438
475
|
recipient: extData.recipient,
|
|
439
476
|
relayer: extData.relayer,
|
|
440
|
-
fee: new
|
|
441
|
-
refund: new
|
|
477
|
+
fee: new BN(extData.fee.toString()),
|
|
478
|
+
refund: new BN(extData.refund.toString()),
|
|
442
479
|
}, {
|
|
443
480
|
proofA: proof.proofA,
|
|
444
481
|
proofB: proof.proofB,
|
|
@@ -463,7 +500,7 @@ async function transact(params) {
|
|
|
463
500
|
systemProgram: web3_js_1.SystemProgram.programId,
|
|
464
501
|
})
|
|
465
502
|
.signers([relayer]);
|
|
466
|
-
return (0,
|
|
503
|
+
return (0, retry_js_1.withRpcRetry)(() => tx.rpc());
|
|
467
504
|
}
|
|
468
505
|
/**
|
|
469
506
|
* Build an unsigned deposit {@link Transaction} for the privacy pool.
|
|
@@ -480,10 +517,10 @@ async function transact(params) {
|
|
|
480
517
|
async function deposit(params) {
|
|
481
518
|
const { program, depositor, amount, mintAddress, recipientPubkey, tree, proofBuilder, treeId = 0, extData: partialExtData, } = params;
|
|
482
519
|
// Create a random keypair for the zero inputs (they don't matter since amount is 0)
|
|
483
|
-
const zeroKeypair = (0,
|
|
520
|
+
const zeroKeypair = (0, utxo_js_1.generateKeypair)();
|
|
484
521
|
// Create zero input UTXOs (for deposits, we don't spend any existing UTXOs)
|
|
485
|
-
const zeroInput0 = (0,
|
|
486
|
-
const zeroInput1 = (0,
|
|
522
|
+
const zeroInput0 = (0, utxo_js_1.createOwnedZeroUTXO)(zeroKeypair.privateKey, mintAddress);
|
|
523
|
+
const zeroInput1 = (0, utxo_js_1.createOwnedZeroUTXO)(zeroKeypair.privateKey, mintAddress);
|
|
487
524
|
// The circuit skips the Merkle proof check for zero-amount inputs (amount == 0
|
|
488
525
|
// causes `enabled = 0` in MerkleProofIfEnabled). So we provide a dummy path using
|
|
489
526
|
// the tree's pre-computed zero chain without inserting anything into the tree.
|
|
@@ -503,12 +540,12 @@ async function deposit(params) {
|
|
|
503
540
|
privateKey: zeroKeypair.privateKey,
|
|
504
541
|
},
|
|
505
542
|
];
|
|
506
|
-
const outputUTXO0 = (0,
|
|
543
|
+
const outputUTXO0 = (0, utxo_js_1.createUTXO)({
|
|
507
544
|
amount: amount,
|
|
508
545
|
pubkey: recipientPubkey,
|
|
509
546
|
mintAddress,
|
|
510
547
|
});
|
|
511
|
-
const outputUTXO1 = (0,
|
|
548
|
+
const outputUTXO1 = (0, utxo_js_1.createUTXO)({
|
|
512
549
|
amount: 0n,
|
|
513
550
|
pubkey: recipientPubkey,
|
|
514
551
|
mintAddress,
|
|
@@ -525,7 +562,7 @@ async function deposit(params) {
|
|
|
525
562
|
refund: partialExtData?.refund ?? 0n,
|
|
526
563
|
claimant: partialExtData?.claimant ?? web3_js_1.SystemProgram.programId,
|
|
527
564
|
};
|
|
528
|
-
const circuitInputs = (0,
|
|
565
|
+
const circuitInputs = (0, proof_js_1.prepareTransactionInputs)({
|
|
529
566
|
inputUTXOs,
|
|
530
567
|
outputUTXOs,
|
|
531
568
|
root: currentRoot,
|
|
@@ -534,7 +571,7 @@ async function deposit(params) {
|
|
|
534
571
|
mintAddress,
|
|
535
572
|
});
|
|
536
573
|
const rawProof = await proofBuilder(circuitInputs);
|
|
537
|
-
const proof = (0,
|
|
574
|
+
const proof = (0, proof_js_1.encodeSnarkjsProofToTransactionProof)(rawProof);
|
|
538
575
|
// -----------------------------------------------------------------------
|
|
539
576
|
// Build the transact instruction inline (duplicated from `transact()`).
|
|
540
577
|
//
|
|
@@ -552,8 +589,8 @@ async function deposit(params) {
|
|
|
552
589
|
const outputTree = getNoteTreePda(program.programId, mintAddress, treeId);
|
|
553
590
|
const nullifierMarker0 = getNullifierMarkerPda(program.programId, mintAddress, circuitInputs.inputNullifiers[0]);
|
|
554
591
|
const nullifierMarker1 = getNullifierMarkerPda(program.programId, mintAddress, circuitInputs.inputNullifiers[1]);
|
|
555
|
-
const extDataHash = (0,
|
|
556
|
-
const isNativeSol = mintAddress.equals(
|
|
592
|
+
const extDataHash = (0, proof_js_1.computeExtDataHash)(extData);
|
|
593
|
+
const isNativeSol = mintAddress.equals(config_js_1.NATIVE_SOL_MINT);
|
|
557
594
|
let vaultTA;
|
|
558
595
|
let userTA;
|
|
559
596
|
let recipientTA;
|
|
@@ -565,12 +602,12 @@ async function deposit(params) {
|
|
|
565
602
|
relayerTA = await (0, spl_token_1.getAssociatedTokenAddress)(mintAddress, depositorPubkey);
|
|
566
603
|
}
|
|
567
604
|
const builtTx = await program.methods
|
|
568
|
-
.transact(Array.from(currentRoot), treeId, treeId, new
|
|
605
|
+
.transact(Array.from(currentRoot), treeId, treeId, new BN(amount.toString()), Array.from(extDataHash), mintAddress, Array.from(circuitInputs.inputNullifiers[0]), Array.from(circuitInputs.inputNullifiers[1]), Array.from(circuitInputs.outputCommitments[0]), Array.from(circuitInputs.outputCommitments[1]), new BN(Math.floor(Date.now() / 1000) + 3600), // 1-hour window from build time
|
|
569
606
|
{
|
|
570
607
|
recipient: extData.recipient,
|
|
571
608
|
relayer: extData.relayer,
|
|
572
|
-
fee: new
|
|
573
|
-
refund: new
|
|
609
|
+
fee: new BN(extData.fee.toString()),
|
|
610
|
+
refund: new BN(extData.refund.toString()),
|
|
574
611
|
}, {
|
|
575
612
|
proofA: proof.proofA,
|
|
576
613
|
proofB: proof.proofB,
|
|
@@ -639,12 +676,12 @@ async function withdraw(params) {
|
|
|
639
676
|
if (changeAmount < 0n) {
|
|
640
677
|
throw new Error("Insufficient input balance for withdrawal");
|
|
641
678
|
}
|
|
642
|
-
const changeUTXO = (0,
|
|
679
|
+
const changeUTXO = (0, utxo_js_1.createUTXO)({
|
|
643
680
|
amount: changeAmount,
|
|
644
681
|
pubkey: changePubkey,
|
|
645
682
|
mintAddress,
|
|
646
683
|
});
|
|
647
|
-
const zeroUTXO = (0,
|
|
684
|
+
const zeroUTXO = (0, utxo_js_1.createUTXO)({
|
|
648
685
|
amount: 0n,
|
|
649
686
|
pubkey: changePubkey,
|
|
650
687
|
mintAddress,
|
|
@@ -659,7 +696,7 @@ async function withdraw(params) {
|
|
|
659
696
|
};
|
|
660
697
|
// publicAmount is negative for withdrawals
|
|
661
698
|
const publicAmount = -withdrawAmount;
|
|
662
|
-
const circuitInputs = (0,
|
|
699
|
+
const circuitInputs = (0, proof_js_1.prepareTransactionInputs)({
|
|
663
700
|
inputUTXOs,
|
|
664
701
|
outputUTXOs,
|
|
665
702
|
root,
|
|
@@ -668,7 +705,7 @@ async function withdraw(params) {
|
|
|
668
705
|
mintAddress,
|
|
669
706
|
});
|
|
670
707
|
const rawProof = await proofBuilder(circuitInputs);
|
|
671
|
-
const proof = (0,
|
|
708
|
+
const proof = (0, proof_js_1.encodeSnarkjsProofToTransactionProof)(rawProof);
|
|
672
709
|
await transact({
|
|
673
710
|
program,
|
|
674
711
|
relayer,
|
|
@@ -710,12 +747,12 @@ async function privateTransfer(params) {
|
|
|
710
747
|
if (totalInput !== totalOutput) {
|
|
711
748
|
throw new Error(`Balance mismatch: inputs=${totalInput}, outputs=${totalOutput}`);
|
|
712
749
|
}
|
|
713
|
-
const outputUTXO0 = (0,
|
|
750
|
+
const outputUTXO0 = (0, utxo_js_1.createUTXO)({
|
|
714
751
|
amount: outputAmounts[0],
|
|
715
752
|
pubkey: outputPubkeys[0],
|
|
716
753
|
mintAddress,
|
|
717
754
|
});
|
|
718
|
-
const outputUTXO1 = (0,
|
|
755
|
+
const outputUTXO1 = (0, utxo_js_1.createUTXO)({
|
|
719
756
|
amount: outputAmounts[1],
|
|
720
757
|
pubkey: outputPubkeys[1],
|
|
721
758
|
mintAddress,
|
|
@@ -734,7 +771,7 @@ async function privateTransfer(params) {
|
|
|
734
771
|
};
|
|
735
772
|
// publicAmount is 0 for transfers
|
|
736
773
|
const publicAmount = 0n;
|
|
737
|
-
const circuitInputs = (0,
|
|
774
|
+
const circuitInputs = (0, proof_js_1.prepareTransactionInputs)({
|
|
738
775
|
inputUTXOs,
|
|
739
776
|
outputUTXOs,
|
|
740
777
|
root,
|
|
@@ -743,7 +780,7 @@ async function privateTransfer(params) {
|
|
|
743
780
|
mintAddress,
|
|
744
781
|
});
|
|
745
782
|
const rawProof = await proofBuilder(circuitInputs);
|
|
746
|
-
const proof = (0,
|
|
783
|
+
const proof = (0, proof_js_1.encodeSnarkjsProofToTransactionProof)(rawProof);
|
|
747
784
|
await transact({
|
|
748
785
|
program,
|
|
749
786
|
relayer,
|
|
@@ -814,7 +851,7 @@ async function fundNativeSource(params) {
|
|
|
814
851
|
const executor = getSwapExecutorPda(program.programId, sourceMint, destMint, inputNullifier0, relayer.publicKey);
|
|
815
852
|
const executorSourceToken = await (0, spl_token_1.getAssociatedTokenAddress)(spl_token_1.NATIVE_MINT, executor, true);
|
|
816
853
|
return program.methods
|
|
817
|
-
.fundNativeSource(sourceMint, destMint, Array.from(inputNullifier0), new
|
|
854
|
+
.fundNativeSource(sourceMint, destMint, Array.from(inputNullifier0), new BN(swapAmount.toString()))
|
|
818
855
|
.accounts({
|
|
819
856
|
executor,
|
|
820
857
|
executorSourceToken,
|
|
@@ -857,17 +894,17 @@ async function transactSwap(params) {
|
|
|
857
894
|
proofB: proof.proofB,
|
|
858
895
|
proofC: proof.proofC,
|
|
859
896
|
}, Array.from(sourceRoot), sourceTreeId, sourceMint, Array.from(inputNullifiers[0]), Array.from(inputNullifiers[1]), destTreeId, destMint, Array.from(outputCommitments[0]), Array.from(outputCommitments[1]), {
|
|
860
|
-
minAmountOut: new
|
|
861
|
-
deadline: new
|
|
897
|
+
minAmountOut: new BN(swapParams.minAmountOut.toString()),
|
|
898
|
+
deadline: new BN(swapParams.deadline.toString()),
|
|
862
899
|
sourceMint: swapParams.sourceMint,
|
|
863
900
|
destMint: swapParams.destMint,
|
|
864
|
-
destAmount: new
|
|
901
|
+
destAmount: new BN(swapParams.destAmount.toString()),
|
|
865
902
|
swapDataHash: Array.from(swapParams.swapDataHash),
|
|
866
|
-
}, new
|
|
903
|
+
}, new BN(swapAmount.toString()), swapData, {
|
|
867
904
|
recipient: extData.recipient,
|
|
868
905
|
relayer: extData.relayer,
|
|
869
|
-
fee: new
|
|
870
|
-
refund: new
|
|
906
|
+
fee: new BN(extData.fee.toString()),
|
|
907
|
+
refund: new BN(extData.refund.toString()),
|
|
871
908
|
})
|
|
872
909
|
.accounts({
|
|
873
910
|
sourceConfig,
|
|
@@ -898,7 +935,7 @@ async function transactSwap(params) {
|
|
|
898
935
|
.instruction();
|
|
899
936
|
const provider = program.provider;
|
|
900
937
|
const tx = new web3_js_1.Transaction();
|
|
901
|
-
if (sourceMint.equals(
|
|
938
|
+
if (sourceMint.equals(config_js_1.NATIVE_SOL_MINT)) {
|
|
902
939
|
const fundIx = await fundNativeSource({
|
|
903
940
|
program,
|
|
904
941
|
relayer,
|
|
@@ -910,5 +947,5 @@ async function transactSwap(params) {
|
|
|
910
947
|
tx.add(fundIx);
|
|
911
948
|
}
|
|
912
949
|
tx.add(swapIx);
|
|
913
|
-
return (0,
|
|
950
|
+
return (0, retry_js_1.withRpcRetry)(() => provider.sendAndConfirm(tx, [relayer]));
|
|
914
951
|
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compact note ciphers — the 80-byte payload carried inside the on-chain
|
|
3
|
+
* `transact` instruction (`note_ciphers: Option<NoteCiphers>`) and re-emitted in
|
|
4
|
+
* `CommitmentEvent`.
|
|
5
|
+
*
|
|
6
|
+
* This is what makes a note recoverable from chain data alone: a recipient who
|
|
7
|
+
* was never told about a deposit can still find it by scanning `view_tag` and
|
|
8
|
+
* decrypting the blob. Without it, note delivery depends on someone calling the
|
|
9
|
+
* relayer, which a third-party integrator cannot be trusted to do.
|
|
10
|
+
*
|
|
11
|
+
* ## Byte-compatibility is load-bearing
|
|
12
|
+
*
|
|
13
|
+
* Four sibling implementations already exist (relayer-server, extension ×2,
|
|
14
|
+
* wallet-app). This is the canonical one, and it MUST agree with them byte for
|
|
15
|
+
* byte in both directions:
|
|
16
|
+
*
|
|
17
|
+
* - disagree on encrypt → new notes are invisible to every existing client
|
|
18
|
+
* - disagree on decrypt → the entire historical note corpus becomes unreadable
|
|
19
|
+
*
|
|
20
|
+
* Three details are easy to get wrong and are individually fatal:
|
|
21
|
+
*
|
|
22
|
+
* 1. The KDF is `nacl.hash(x).slice(0, 32)` — **truncated SHA-512**, despite the
|
|
23
|
+
* sibling helper being named `sha256ForNotes`. See `kdfSHA512_256` in utxo.ts.
|
|
24
|
+
* 2. The ephemeral key published on-chain is **Ed25519**; it is converted to
|
|
25
|
+
* X25519 only for the Diffie-Hellman. Scanners convert it back.
|
|
26
|
+
* 3. `viewTag` is the first byte of the hash of the **raw shared secret**, not of
|
|
27
|
+
* the derived key. A wrong view tag makes every note invisible to the scan
|
|
28
|
+
* loop, which rejects ~255/256 candidates on this byte alone.
|
|
29
|
+
*
|
|
30
|
+
* The ECDH primitives are imported from `utxo.ts` rather than reimplemented — a
|
|
31
|
+
* fifth independent Edwards→Montgomery implementation is exactly how the current
|
|
32
|
+
* drift risk between the four siblings arose.
|
|
33
|
+
*
|
|
34
|
+
* Verified against the siblings by `tests/compact-note.test.ts` (golden vectors)
|
|
35
|
+
* and `scripts/verify-compact-cipher-parity.mjs` (differential).
|
|
36
|
+
*/
|
|
37
|
+
/** Size of the on-chain compact blob: nonce[24] || secretbox(40-byte plaintext)[56]. */
|
|
38
|
+
export declare const COMPACT_BLOB_LENGTH = 80;
|
|
39
|
+
export type CompactNoteCipher = {
|
|
40
|
+
/** Ed25519 ephemeral public key, published on-chain. */
|
|
41
|
+
ephemeralPublicKey: Uint8Array;
|
|
42
|
+
/** 80 bytes: nonce[24] || ciphertext[56]. */
|
|
43
|
+
compactBlob: Uint8Array;
|
|
44
|
+
/** 1-byte scan filter. */
|
|
45
|
+
viewTag: number;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Test-only determinism hook. NOT part of the public contract — it exists so
|
|
49
|
+
* golden vectors can pin exact bytes against the sibling implementations, which
|
|
50
|
+
* is the only way to prove byte-compatibility rather than assume it.
|
|
51
|
+
*
|
|
52
|
+
* @internal
|
|
53
|
+
*/
|
|
54
|
+
export type CompactNoteDeterminism = {
|
|
55
|
+
ephemeralSeed: Uint8Array;
|
|
56
|
+
nonce: Uint8Array;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Encrypt a note's secret material to a recipient's Solana wallet key.
|
|
60
|
+
*
|
|
61
|
+
* The recipient does not need to be the signer of the transaction, or to have
|
|
62
|
+
* any prior relationship with the sender — this is what makes shielding to an
|
|
63
|
+
* arbitrary owner possible.
|
|
64
|
+
*
|
|
65
|
+
* @param recipientWalletPubkey 32-byte Ed25519 Solana wallet public key
|
|
66
|
+
* @param blinding 32-byte big-endian blinding factor
|
|
67
|
+
* @param amount note amount in base units
|
|
68
|
+
*/
|
|
69
|
+
export declare function createCompactNoteCipher(recipientWalletPubkey: Uint8Array, blinding: Uint8Array, amount: bigint,
|
|
70
|
+
/** @internal */ __det?: CompactNoteDeterminism): CompactNoteCipher;
|
|
71
|
+
/**
|
|
72
|
+
* Recover a note's blinding and amount from an on-chain compact cipher.
|
|
73
|
+
*
|
|
74
|
+
* Returns `null` when the blob was not encrypted to this key — the expected
|
|
75
|
+
* outcome for the overwhelming majority of candidates during a scan, so callers
|
|
76
|
+
* should filter on `viewTag` first (see {@link matchesViewTag}) and only attempt
|
|
77
|
+
* decryption on the ~1/256 that survive.
|
|
78
|
+
*
|
|
79
|
+
* @param walletSecretKey 32-byte Ed25519 seed or 64-byte secret key
|
|
80
|
+
*/
|
|
81
|
+
export declare function decryptCompactNoteCipher(walletSecretKey: Uint8Array, ephemeralPublicKey: Uint8Array, compactBlob: Uint8Array, precomputed?: {
|
|
82
|
+
sharedSecret?: Uint8Array;
|
|
83
|
+
x25519Private?: Uint8Array;
|
|
84
|
+
}): {
|
|
85
|
+
blinding: Uint8Array;
|
|
86
|
+
amount: bigint;
|
|
87
|
+
} | null;
|
|
88
|
+
/**
|
|
89
|
+
* Cheap scan filter: does this on-chain cipher plausibly belong to us?
|
|
90
|
+
*
|
|
91
|
+
* Rejects ~255/256 candidates for the cost of one scalar multiplication and one
|
|
92
|
+
* hash, versus a full secretbox open. Hoist `x25519Private` across a scan loop
|
|
93
|
+
* with {@link toX25519Private}.
|
|
94
|
+
*/
|
|
95
|
+
export declare function matchesViewTag(walletSecretKey: Uint8Array, ephemeralPublicKey: Uint8Array, viewTag: number, precomputed?: {
|
|
96
|
+
x25519Private?: Uint8Array;
|
|
97
|
+
}): {
|
|
98
|
+
matches: boolean;
|
|
99
|
+
sharedSecret: Uint8Array;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Convert an Ed25519 wallet secret key to its X25519 scalar once, for reuse
|
|
103
|
+
* across a scan loop.
|
|
104
|
+
*/
|
|
105
|
+
export declare function toX25519Private(walletSecretKey: Uint8Array): Uint8Array;
|
|
106
|
+
/** The all-zero cipher the program treats as "no note here". */
|
|
107
|
+
export declare function emptyNoteCipher(): CompactNoteCipher;
|