@veilo/sdk-core 0.5.0 → 0.7.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 +282 -18
- package/accounts.d.ts +2 -0
- package/accounts.js +3 -0
- package/cloak.d.ts +2 -0
- package/cloak.js +3 -0
- package/config.d.ts +2 -0
- package/config.js +3 -0
- package/dist/cjs/events/index.d.ts +6 -0
- package/dist/cjs/events/index.js +22 -0
- package/dist/cjs/events/parsing.d.ts +25 -0
- package/dist/cjs/events/parsing.js +158 -0
- package/dist/cjs/events/position.d.ts +26 -0
- package/dist/cjs/events/position.js +53 -0
- package/dist/cjs/events/recovery.d.ts +87 -0
- package/dist/cjs/events/recovery.js +281 -0
- package/dist/cjs/events/scanning.d.ts +35 -0
- package/dist/cjs/events/scanning.js +98 -0
- package/dist/cjs/events/tree.d.ts +27 -0
- package/dist/cjs/events/tree.js +63 -0
- package/dist/cjs/events/types.d.ts +39 -0
- package/dist/cjs/events/types.js +2 -0
- package/dist/cjs/events.d.ts +1 -77
- package/dist/cjs/events.js +6 -154
- package/dist/cjs/identity/encoding.d.ts +9 -0
- package/dist/cjs/identity/encoding.js +85 -0
- package/dist/cjs/identity/index.d.ts +4 -0
- package/dist/cjs/identity/index.js +20 -0
- package/dist/cjs/identity/noteKey.d.ts +33 -0
- package/dist/cjs/identity/noteKey.js +97 -0
- package/dist/cjs/identity/spending.d.ts +18 -0
- package/dist/cjs/identity/spending.js +46 -0
- package/dist/cjs/identity/viewKey.d.ts +38 -0
- package/dist/cjs/identity/viewKey.js +105 -0
- package/dist/cjs/idl/index.d.ts +18 -0
- package/dist/cjs/idl/index.js +24 -0
- package/dist/cjs/idl/privacy_pool.json +9907 -9907
- package/dist/cjs/idl/privacy_pool_legacy.d.ts +5 -0
- package/dist/cjs/idl/privacy_pool_legacy.js +3307 -0
- package/dist/cjs/idl/privacy_pool_legacy.json +2302 -0
- package/dist/cjs/idl/privacy_pool_legacy2.d.ts +5 -0
- package/dist/cjs/idl/privacy_pool_legacy2.js +3397 -0
- package/dist/cjs/idl/privacy_pool_legacy2.json +2376 -0
- package/dist/cjs/index.d.ts +8 -3
- package/dist/cjs/index.js +12 -2
- package/dist/cjs/notes/amount.d.ts +2 -0
- package/dist/cjs/notes/amount.js +21 -0
- package/dist/cjs/notes/index.d.ts +4 -0
- package/dist/cjs/notes/index.js +4 -0
- package/dist/cjs/notes/recovery.d.ts +16 -6
- package/dist/cjs/notes/recovery.js +33 -16
- package/dist/cjs/notes/scan.d.ts +74 -0
- package/dist/cjs/notes/scan.js +110 -0
- package/dist/cjs/notes/selection.d.ts +133 -0
- package/dist/cjs/notes/selection.js +260 -0
- package/dist/cjs/notes/spent.d.ts +60 -0
- package/dist/cjs/notes/spent.js +101 -0
- package/dist/cjs/perps/constants.d.ts +16 -0
- package/dist/cjs/perps/constants.js +52 -0
- package/dist/cjs/perps/index.d.ts +5 -0
- package/dist/cjs/perps/index.js +21 -0
- package/dist/cjs/perps/keys.d.ts +16 -0
- package/dist/cjs/perps/keys.js +43 -0
- package/dist/cjs/perps/pdas.d.ts +29 -0
- package/dist/cjs/perps/pdas.js +55 -0
- package/dist/cjs/perps/relayer.d.ts +9 -0
- package/dist/cjs/perps/relayer.js +35 -0
- package/dist/cjs/perps/types.d.ts +126 -0
- package/dist/cjs/perps/types.js +2 -0
- package/dist/cjs/positions/index.d.ts +4 -0
- package/dist/cjs/positions/index.js +20 -0
- package/dist/cjs/positions/keys.d.ts +29 -0
- package/dist/cjs/positions/keys.js +71 -0
- package/dist/cjs/positions/pdas.d.ts +15 -0
- package/dist/cjs/positions/pdas.js +54 -0
- package/dist/cjs/positions/relayer.d.ts +4 -0
- package/dist/cjs/positions/relayer.js +15 -0
- package/dist/cjs/positions/types.d.ts +97 -0
- package/dist/cjs/positions/types.js +2 -0
- package/dist/cjs/predictions/client.d.ts +41 -0
- package/dist/cjs/predictions/client.js +147 -0
- package/dist/cjs/predictions/index.d.ts +5 -0
- package/dist/cjs/predictions/index.js +21 -0
- package/dist/cjs/predictions/keys.d.ts +14 -0
- package/dist/cjs/predictions/keys.js +40 -0
- package/dist/cjs/predictions/pdas.d.ts +6 -0
- package/dist/cjs/predictions/pdas.js +13 -0
- package/dist/cjs/predictions/relayer.d.ts +3 -0
- package/dist/cjs/predictions/relayer.js +11 -0
- package/dist/cjs/predictions/types.d.ts +178 -0
- package/dist/cjs/predictions/types.js +2 -0
- package/dist/cjs/program.d.ts +2 -0
- package/dist/cjs/program.js +8 -7
- package/dist/cjs/prover.d.ts +27 -4
- package/dist/cjs/prover.js +107 -48
- package/dist/cjs/relayer/api.d.ts +41 -0
- package/dist/cjs/relayer/api.js +99 -0
- package/dist/cjs/relayer/client.d.ts +42 -17
- package/dist/cjs/relayer/client.js +94 -17
- package/dist/cjs/relayer/errors.d.ts +1 -1
- package/dist/cjs/relayer/errors.js +1 -1
- package/dist/cjs/relayer/index.d.ts +4 -1
- package/dist/cjs/relayer/index.js +1 -1
- package/dist/cjs/relayer/internal-config.d.ts +36 -0
- package/dist/cjs/relayer/internal-config.js +9 -0
- package/dist/cjs/relayer/internal.d.ts +6 -0
- package/dist/cjs/relayer/internal.js +14 -0
- package/dist/cjs/relayer/transport.d.ts +4 -2
- package/dist/cjs/relayer/transport.js +10 -5
- package/dist/cjs/relayer/types.d.ts +40 -10
- package/dist/cjs/relayer/types.js +1 -1
- package/dist/cjs/shield/owner.js +1 -4
- package/dist/esm/events/index.d.ts +6 -0
- package/dist/esm/events/index.js +6 -0
- package/dist/esm/events/parsing.d.ts +25 -0
- package/dist/esm/events/parsing.js +120 -0
- package/dist/esm/events/position.d.ts +26 -0
- package/dist/esm/events/position.js +48 -0
- package/dist/esm/events/recovery.d.ts +87 -0
- package/dist/esm/events/recovery.js +237 -0
- package/dist/esm/events/scanning.d.ts +35 -0
- package/dist/esm/events/scanning.js +92 -0
- package/dist/esm/events/tree.d.ts +27 -0
- package/dist/esm/events/tree.js +58 -0
- package/dist/esm/events/types.d.ts +39 -0
- package/dist/esm/events/types.js +1 -0
- package/dist/esm/events.d.ts +1 -77
- package/dist/esm/events.js +3 -129
- package/dist/esm/identity/encoding.d.ts +9 -0
- package/dist/esm/identity/encoding.js +75 -0
- package/dist/esm/identity/index.d.ts +4 -0
- package/dist/esm/identity/index.js +4 -0
- package/dist/esm/identity/noteKey.d.ts +33 -0
- package/dist/esm/identity/noteKey.js +91 -0
- package/dist/esm/identity/spending.d.ts +18 -0
- package/dist/esm/identity/spending.js +35 -0
- package/dist/esm/identity/viewKey.d.ts +38 -0
- package/dist/esm/identity/viewKey.js +96 -0
- package/dist/esm/idl/index.d.ts +18 -0
- package/dist/esm/idl/index.js +18 -0
- package/dist/esm/idl/privacy_pool.json +9907 -9907
- package/dist/esm/idl/privacy_pool_legacy.d.ts +5 -0
- package/dist/esm/idl/privacy_pool_legacy.js +3305 -0
- package/dist/esm/idl/privacy_pool_legacy.json +2302 -0
- package/dist/esm/idl/privacy_pool_legacy2.d.ts +5 -0
- package/dist/esm/idl/privacy_pool_legacy2.js +3395 -0
- package/dist/esm/idl/privacy_pool_legacy2.json +2376 -0
- package/dist/esm/index.d.ts +8 -3
- package/dist/esm/index.js +8 -3
- package/dist/esm/notes/amount.d.ts +2 -0
- package/dist/esm/notes/amount.js +18 -0
- package/dist/esm/notes/index.d.ts +4 -0
- package/dist/esm/notes/index.js +4 -0
- package/dist/esm/notes/recovery.d.ts +16 -6
- package/dist/esm/notes/recovery.js +33 -16
- package/dist/esm/notes/scan.d.ts +74 -0
- package/dist/esm/notes/scan.js +105 -0
- package/dist/esm/notes/selection.d.ts +133 -0
- package/dist/esm/notes/selection.js +254 -0
- package/dist/esm/notes/spent.d.ts +60 -0
- package/dist/esm/notes/spent.js +96 -0
- package/dist/esm/perps/constants.d.ts +16 -0
- package/dist/esm/perps/constants.js +48 -0
- package/dist/esm/perps/index.d.ts +5 -0
- package/dist/esm/perps/index.js +5 -0
- package/dist/esm/perps/keys.d.ts +16 -0
- package/dist/esm/perps/keys.js +35 -0
- package/dist/esm/perps/pdas.d.ts +29 -0
- package/dist/esm/perps/pdas.js +47 -0
- package/dist/esm/perps/relayer.d.ts +9 -0
- package/dist/esm/perps/relayer.js +25 -0
- package/dist/esm/perps/types.d.ts +126 -0
- package/dist/esm/perps/types.js +1 -0
- package/dist/esm/positions/index.d.ts +4 -0
- package/dist/esm/positions/index.js +4 -0
- package/dist/esm/positions/keys.d.ts +29 -0
- package/dist/esm/positions/keys.js +65 -0
- package/dist/esm/positions/pdas.d.ts +15 -0
- package/dist/esm/positions/pdas.js +44 -0
- package/dist/esm/positions/relayer.d.ts +4 -0
- package/dist/esm/positions/relayer.js +10 -0
- package/dist/esm/positions/types.d.ts +97 -0
- package/dist/esm/positions/types.js +1 -0
- package/dist/esm/predictions/client.d.ts +41 -0
- package/dist/esm/predictions/client.js +141 -0
- package/dist/esm/predictions/index.d.ts +5 -0
- package/dist/esm/predictions/index.js +5 -0
- package/dist/esm/predictions/keys.d.ts +14 -0
- package/dist/esm/predictions/keys.js +33 -0
- package/dist/esm/predictions/pdas.d.ts +6 -0
- package/dist/esm/predictions/pdas.js +10 -0
- package/dist/esm/predictions/relayer.d.ts +3 -0
- package/dist/esm/predictions/relayer.js +7 -0
- package/dist/esm/predictions/types.d.ts +178 -0
- package/dist/esm/predictions/types.js +1 -0
- package/dist/esm/program.d.ts +2 -0
- package/dist/esm/program.js +4 -3
- package/dist/esm/prover.d.ts +27 -4
- package/dist/esm/prover.js +106 -15
- package/dist/esm/relayer/api.d.ts +41 -0
- package/dist/esm/relayer/api.js +66 -0
- package/dist/esm/relayer/client.d.ts +42 -17
- package/dist/esm/relayer/client.js +94 -17
- package/dist/esm/relayer/errors.d.ts +1 -1
- package/dist/esm/relayer/errors.js +1 -1
- package/dist/esm/relayer/index.d.ts +4 -1
- package/dist/esm/relayer/index.js +1 -1
- package/dist/esm/relayer/internal-config.d.ts +36 -0
- package/dist/esm/relayer/internal-config.js +6 -0
- package/dist/esm/relayer/internal.d.ts +6 -0
- package/dist/esm/relayer/internal.js +10 -0
- package/dist/esm/relayer/transport.d.ts +4 -2
- package/dist/esm/relayer/transport.js +10 -5
- package/dist/esm/relayer/types.d.ts +40 -10
- package/dist/esm/relayer/types.js +1 -1
- package/dist/esm/shield/owner.js +1 -4
- package/identity.d.ts +2 -0
- package/identity.js +3 -0
- package/idl.d.ts +2 -0
- package/idl.js +3 -0
- package/notes.d.ts +2 -0
- package/notes.js +3 -0
- package/package.json +85 -3
- package/perps.d.ts +2 -0
- package/perps.js +3 -0
- package/poseidon.d.ts +2 -0
- package/poseidon.js +3 -0
- package/positions.d.ts +2 -0
- package/positions.js +3 -0
- package/predictions.d.ts +2 -0
- package/predictions.js +3 -0
- package/proof.d.ts +2 -0
- package/proof.js +3 -0
- package/prover.d.ts +2 -0
- package/prover.js +3 -0
- package/relayer.d.ts +2 -0
- package/relayer.js +3 -0
- package/shield.d.ts +2 -0
- package/shield.js +3 -0
- package/transactions.d.ts +2 -0
- package/transactions.js +3 -0
package/dist/esm/index.d.ts
CHANGED
|
@@ -4,10 +4,15 @@ export * from "./config.js";
|
|
|
4
4
|
export * from "./merkle.js";
|
|
5
5
|
export * from "./proof.js";
|
|
6
6
|
export * from "./events.js";
|
|
7
|
-
export
|
|
7
|
+
export * from "./identity/index.js";
|
|
8
|
+
export * from "./positions/index.js";
|
|
9
|
+
export * from "./perps/index.js";
|
|
10
|
+
export * from "./predictions/index.js";
|
|
11
|
+
export { createVeiloProgram, createReadonlyVeiloProgram, PRIVACY_POOL_CURRENT_IDL, PRIVACY_POOL_IDL, PRIVACY_POOL_IDLS, PRIVACY_POOL_LEGACY_IDL, PRIVACY_POOL_LEGACY2_IDL, PRIVACY_POOL_PROGRAM_ID, } from "./program.js";
|
|
12
|
+
export type { PrivacyPoolIdlVersion } from "./idl/index.js";
|
|
8
13
|
export * from "./shield/index.js";
|
|
9
|
-
export { createTransactionProver, createSwapProver, verifyProof, } from "./prover.js";
|
|
10
|
-
export type { CircuitArtifacts } from "./prover.js";
|
|
14
|
+
export { createTransactionProver, createSwapProver, resolveCircuitArtifacts, verifyProof, } from "./prover.js";
|
|
15
|
+
export type { CircuitArtifacts, CircuitName } from "./prover.js";
|
|
11
16
|
export * from "./compactNote.js";
|
|
12
17
|
export { randomBytes, randomBytes32 } from "./random.js";
|
|
13
18
|
export * from "./relayer.js";
|
package/dist/esm/index.js
CHANGED
|
@@ -10,18 +10,23 @@ export * from "./merkle.js";
|
|
|
10
10
|
export * from "./proof.js";
|
|
11
11
|
// On-chain event scanning and tree reconstruction
|
|
12
12
|
export * from "./events.js";
|
|
13
|
+
// Private product helpers: keys, PDAs, request types, and unsigned API clients.
|
|
14
|
+
export * from "./identity/index.js";
|
|
15
|
+
export * from "./positions/index.js";
|
|
16
|
+
export * from "./perps/index.js";
|
|
17
|
+
export * from "./predictions/index.js";
|
|
13
18
|
// Program factory and IDL
|
|
14
|
-
export { createVeiloProgram, createReadonlyVeiloProgram, PRIVACY_POOL_IDL, PRIVACY_POOL_PROGRAM_ID, } from "./program.js";
|
|
19
|
+
export { createVeiloProgram, createReadonlyVeiloProgram, PRIVACY_POOL_CURRENT_IDL, PRIVACY_POOL_IDL, PRIVACY_POOL_IDLS, PRIVACY_POOL_LEGACY_IDL, PRIVACY_POOL_LEGACY2_IDL, PRIVACY_POOL_PROGRAM_ID, } from "./program.js";
|
|
15
20
|
// Wallet-agnostic shielding — build an unsigned deposit for any external wallet
|
|
16
21
|
export * from "./shield/index.js";
|
|
17
22
|
// Proof generation (requires snarkjs peer dependency)
|
|
18
|
-
export { createTransactionProver, createSwapProver, verifyProof, } from "./prover.js";
|
|
23
|
+
export { createTransactionProver, createSwapProver, resolveCircuitArtifacts, verifyProof, } from "./prover.js";
|
|
19
24
|
// Compact note ciphers — the on-chain payload that makes notes recoverable
|
|
20
25
|
// from chain data alone, without any relayer involvement.
|
|
21
26
|
export * from "./compactNote.js";
|
|
22
27
|
// Cross-platform CSPRNG (no node:crypto import — safe in browser bundles)
|
|
23
28
|
export { randomBytes, randomBytes32 } from "./random.js";
|
|
24
|
-
//
|
|
29
|
+
// Managed relayer API helpers (network configuration remains internal)
|
|
25
30
|
export * from "./relayer.js";
|
|
26
31
|
// Veilo Cloak Partner API client and helpers
|
|
27
32
|
export * from "./cloak.js";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Coerce a note amount to base units without ever going through Number. */
|
|
2
|
+
export function toBaseUnits(amount, label) {
|
|
3
|
+
if (typeof amount === "bigint")
|
|
4
|
+
return amount;
|
|
5
|
+
if (typeof amount === "number") {
|
|
6
|
+
if (!Number.isSafeInteger(amount)) {
|
|
7
|
+
throw new Error(`${label} ${amount} is not a safe integer; pass a bigint or a decimal ` +
|
|
8
|
+
"string for token amounts.");
|
|
9
|
+
}
|
|
10
|
+
return BigInt(amount);
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
return BigInt(amount);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
throw new Error(`${label} ${JSON.stringify(amount)} is not a valid amount.`);
|
|
17
|
+
}
|
|
18
|
+
}
|
package/dist/esm/notes/index.js
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import type { DecryptedNote } from "./mailbox.js";
|
|
2
2
|
/**
|
|
3
|
-
* Fetch
|
|
4
|
-
* and decrypt them using the wallet's secret key.
|
|
3
|
+
* Fetch this account's encrypted notes from the relayer and decrypt them.
|
|
5
4
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Goes through the managed relayer helper, so the network coordinates stay
|
|
6
|
+
* internal — there is no URL to pass. Notes that fail to decrypt are skipped:
|
|
7
|
+
* a query can return rows addressed to other keys, and one bad row must not
|
|
8
|
+
* lose the rest.
|
|
9
|
+
*
|
|
10
|
+
* @param authToken token from `register()` / `restore()`
|
|
11
|
+
* @param walletSecretKey full 64-byte keypair.secretKey, used for decryption
|
|
12
|
+
* @param walletPublicKey optional base58 filter, narrowing the query server-side
|
|
9
13
|
*/
|
|
10
|
-
export declare function fetchAndDecryptNotes(
|
|
14
|
+
export declare function fetchAndDecryptNotes(params: {
|
|
15
|
+
authToken: string;
|
|
16
|
+
walletSecretKey: Uint8Array;
|
|
17
|
+
walletPublicKey?: string;
|
|
18
|
+
limit?: number;
|
|
19
|
+
offset?: number;
|
|
20
|
+
}): Promise<DecryptedNote[]>;
|
|
@@ -1,28 +1,45 @@
|
|
|
1
|
+
import { queryEncryptedNotes } from "../relayer/api.js";
|
|
1
2
|
import { decryptBlindMailboxNote } from "./mailbox.js";
|
|
2
3
|
/**
|
|
3
|
-
* Fetch
|
|
4
|
-
* and decrypt them using the wallet's secret key.
|
|
4
|
+
* Fetch this account's encrypted notes from the relayer and decrypt them.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* Goes through the managed relayer helper, so the network coordinates stay
|
|
7
|
+
* internal — there is no URL to pass. Notes that fail to decrypt are skipped:
|
|
8
|
+
* a query can return rows addressed to other keys, and one bad row must not
|
|
9
|
+
* lose the rest.
|
|
10
|
+
*
|
|
11
|
+
* @param authToken token from `register()` / `restore()`
|
|
12
|
+
* @param walletSecretKey full 64-byte keypair.secretKey, used for decryption
|
|
13
|
+
* @param walletPublicKey optional base58 filter, narrowing the query server-side
|
|
9
14
|
*/
|
|
10
|
-
export async function fetchAndDecryptNotes(
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
export async function fetchAndDecryptNotes(params) {
|
|
16
|
+
const { authToken, walletSecretKey, walletPublicKey, limit, offset } = params;
|
|
17
|
+
const { notes } = await queryEncryptedNotes(authToken, {
|
|
18
|
+
...(walletPublicKey ? { walletPublicKey } : {}),
|
|
19
|
+
...(limit === undefined ? {} : { limit }),
|
|
20
|
+
...(offset === undefined ? {} : { offset }),
|
|
21
|
+
});
|
|
16
22
|
const results = [];
|
|
17
|
-
for (const note of notes) {
|
|
23
|
+
for (const note of notes ?? []) {
|
|
24
|
+
if (!note.ephemeralPublicKey || !note.encryptedBlob)
|
|
25
|
+
continue;
|
|
18
26
|
try {
|
|
19
|
-
|
|
20
|
-
const decrypted = decryptBlindMailboxNote(walletSecretKey, new Uint8Array(epk), note.encryptedBlob);
|
|
21
|
-
results.push(decrypted);
|
|
27
|
+
results.push(decryptBlindMailboxNote(walletSecretKey, base64ToBytes(note.ephemeralPublicKey), note.encryptedBlob));
|
|
22
28
|
}
|
|
23
29
|
catch {
|
|
24
|
-
//
|
|
30
|
+
// Not addressed to this key, or corrupted — skip it.
|
|
25
31
|
}
|
|
26
32
|
}
|
|
27
33
|
return results;
|
|
28
34
|
}
|
|
35
|
+
/** Base64 → bytes without assuming a Node Buffer global. */
|
|
36
|
+
function base64ToBytes(value) {
|
|
37
|
+
if (typeof atob === "function") {
|
|
38
|
+
const binary = atob(value);
|
|
39
|
+
const out = new Uint8Array(binary.length);
|
|
40
|
+
for (let i = 0; i < binary.length; i++)
|
|
41
|
+
out[i] = binary.charCodeAt(i);
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
return new Uint8Array(Buffer.from(value, "base64"));
|
|
45
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { CompactScanNote, RelayerEncryptedNote } from "../relayer/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Finding your own notes in the public feed.
|
|
4
|
+
*
|
|
5
|
+
* The compact feed is deliberately anonymous: every row is ciphertext plus a
|
|
6
|
+
* one-byte view tag, and nothing on it says who a note belongs to. You find
|
|
7
|
+
* yours by trial decryption. The view tag exists to make that cheap — it
|
|
8
|
+
* rejects ~255/256 of other people's rows with one hash instead of a full
|
|
9
|
+
* decrypt.
|
|
10
|
+
*/
|
|
11
|
+
/** Server-side page cap (notes.constants COMPACT_SCAN_MAX_LIMIT). */
|
|
12
|
+
export declare const COMPACT_SCAN_MAX_LIMIT = 1000;
|
|
13
|
+
/** Server-side cap per fetch-by-commitment request. */
|
|
14
|
+
export declare const MAX_COMMITMENTS_PER_FETCH = 50;
|
|
15
|
+
/** A feed row that decrypted under your key. */
|
|
16
|
+
export interface ScannedNote {
|
|
17
|
+
/** Value in base units. */
|
|
18
|
+
amount: bigint;
|
|
19
|
+
/** 32-byte blinding factor recovered from the cipher. */
|
|
20
|
+
blinding: Uint8Array;
|
|
21
|
+
/** Commitment hex, as stored. */
|
|
22
|
+
commitment: string;
|
|
23
|
+
leafIndex?: number | null;
|
|
24
|
+
treeId?: number | null;
|
|
25
|
+
mint?: string | null;
|
|
26
|
+
timestamp?: number;
|
|
27
|
+
txSignature?: string;
|
|
28
|
+
/** The raw feed row this came from. */
|
|
29
|
+
source: CompactScanNote;
|
|
30
|
+
}
|
|
31
|
+
export interface ScanCompactNotesOptions {
|
|
32
|
+
/** Rows per request. Capped at COMPACT_SCAN_MAX_LIMIT. */
|
|
33
|
+
pageSize?: number;
|
|
34
|
+
/** Resume from a previous run's `nextCursor`. */
|
|
35
|
+
cursor?: string;
|
|
36
|
+
/** Stop after this many pages. Unlimited by default. */
|
|
37
|
+
maxPages?: number;
|
|
38
|
+
/** Called after each page, for progress reporting. Return false to stop. */
|
|
39
|
+
onPage?: (progress: {
|
|
40
|
+
scanned: number;
|
|
41
|
+
matched: number;
|
|
42
|
+
cursor: string | null;
|
|
43
|
+
}) => boolean | void;
|
|
44
|
+
}
|
|
45
|
+
export interface ScanCompactNotesResult {
|
|
46
|
+
/** Rows that decrypted under your key. */
|
|
47
|
+
notes: ScannedNote[];
|
|
48
|
+
/** Feed rows examined. */
|
|
49
|
+
scanned: number;
|
|
50
|
+
/**
|
|
51
|
+
* Cursor to resume from. Null means the feed was exhausted; a string means
|
|
52
|
+
* scanning stopped early (maxPages, or onPage returning false).
|
|
53
|
+
*/
|
|
54
|
+
nextCursor: string | null;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Walk the compact feed and return the notes that belong to this key.
|
|
58
|
+
*
|
|
59
|
+
* Paginates to exhaustion by default. The X25519 private key is derived once
|
|
60
|
+
* and reused across every row rather than per row — that derivation dominates
|
|
61
|
+
* the cost otherwise.
|
|
62
|
+
*
|
|
63
|
+
* @param walletSecretKey full 64-byte keypair.secretKey
|
|
64
|
+
*/
|
|
65
|
+
export declare function scanCompactNotes(walletSecretKey: Uint8Array, options?: ScanCompactNotesOptions): Promise<ScanCompactNotesResult>;
|
|
66
|
+
/**
|
|
67
|
+
* Look up stored notes by commitment, chunked to the server's limit.
|
|
68
|
+
*
|
|
69
|
+
* Requests are issued concurrently. Commitments the relayer does not know are
|
|
70
|
+
* simply absent from the result — the array is not positional.
|
|
71
|
+
*/
|
|
72
|
+
export declare function fetchNotesByCommitment(commitments: string[], options?: {
|
|
73
|
+
walletPublicKey?: string;
|
|
74
|
+
}): Promise<RelayerEncryptedNote[]>;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { compactScan, fetchNotesByCommitmentPage, } from "../relayer/api.js";
|
|
2
|
+
import { decryptCompactNoteCipher, matchesViewTag, toX25519Private, } from "../compactNote.js";
|
|
3
|
+
/**
|
|
4
|
+
* Finding your own notes in the public feed.
|
|
5
|
+
*
|
|
6
|
+
* The compact feed is deliberately anonymous: every row is ciphertext plus a
|
|
7
|
+
* one-byte view tag, and nothing on it says who a note belongs to. You find
|
|
8
|
+
* yours by trial decryption. The view tag exists to make that cheap — it
|
|
9
|
+
* rejects ~255/256 of other people's rows with one hash instead of a full
|
|
10
|
+
* decrypt.
|
|
11
|
+
*/
|
|
12
|
+
/** Server-side page cap (notes.constants COMPACT_SCAN_MAX_LIMIT). */
|
|
13
|
+
export const COMPACT_SCAN_MAX_LIMIT = 1000;
|
|
14
|
+
/** Server-side cap per fetch-by-commitment request. */
|
|
15
|
+
export const MAX_COMMITMENTS_PER_FETCH = 50;
|
|
16
|
+
function decodeBase64(value) {
|
|
17
|
+
if (typeof atob === "function") {
|
|
18
|
+
const binary = atob(value);
|
|
19
|
+
const out = new Uint8Array(binary.length);
|
|
20
|
+
for (let i = 0; i < binary.length; i++)
|
|
21
|
+
out[i] = binary.charCodeAt(i);
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
return new Uint8Array(Buffer.from(value, "base64"));
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Walk the compact feed and return the notes that belong to this key.
|
|
28
|
+
*
|
|
29
|
+
* Paginates to exhaustion by default. The X25519 private key is derived once
|
|
30
|
+
* and reused across every row rather than per row — that derivation dominates
|
|
31
|
+
* the cost otherwise.
|
|
32
|
+
*
|
|
33
|
+
* @param walletSecretKey full 64-byte keypair.secretKey
|
|
34
|
+
*/
|
|
35
|
+
export async function scanCompactNotes(walletSecretKey, options = {}) {
|
|
36
|
+
const pageSize = Math.min(options.pageSize ?? 500, COMPACT_SCAN_MAX_LIMIT);
|
|
37
|
+
const x25519Private = toX25519Private(walletSecretKey);
|
|
38
|
+
const notes = [];
|
|
39
|
+
let cursor = options.cursor;
|
|
40
|
+
let scanned = 0;
|
|
41
|
+
let pages = 0;
|
|
42
|
+
for (;;) {
|
|
43
|
+
const page = await compactScan({ limit: pageSize, cursor });
|
|
44
|
+
pages++;
|
|
45
|
+
for (const row of page.notes ?? []) {
|
|
46
|
+
if (!row.compactEphemeralKey || !row.compactBlob)
|
|
47
|
+
continue;
|
|
48
|
+
scanned++;
|
|
49
|
+
const ephemeralPublicKey = decodeBase64(row.compactEphemeralKey);
|
|
50
|
+
// View tag first: one hash rejects almost every row that is not ours.
|
|
51
|
+
const { matches, sharedSecret } = matchesViewTag(walletSecretKey, ephemeralPublicKey, row.viewTag ?? -1, { x25519Private });
|
|
52
|
+
if (!matches)
|
|
53
|
+
continue;
|
|
54
|
+
const opened = decryptCompactNoteCipher(walletSecretKey, ephemeralPublicKey, decodeBase64(row.compactBlob), { sharedSecret, x25519Private });
|
|
55
|
+
if (!opened)
|
|
56
|
+
continue; // view-tag collision, ~1 in 256
|
|
57
|
+
notes.push({
|
|
58
|
+
amount: opened.amount,
|
|
59
|
+
blinding: opened.blinding,
|
|
60
|
+
commitment: row.commitment,
|
|
61
|
+
leafIndex: row.leafIndex,
|
|
62
|
+
treeId: row.treeId,
|
|
63
|
+
mint: row.mintAddress,
|
|
64
|
+
timestamp: row.timestamp,
|
|
65
|
+
txSignature: row.txSignature,
|
|
66
|
+
source: row,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
cursor = page.nextCursor ?? undefined;
|
|
70
|
+
const keepGoing = options.onPage?.({
|
|
71
|
+
scanned,
|
|
72
|
+
matched: notes.length,
|
|
73
|
+
cursor: page.nextCursor,
|
|
74
|
+
});
|
|
75
|
+
if (keepGoing === false)
|
|
76
|
+
break;
|
|
77
|
+
if (!page.hasMore || !cursor)
|
|
78
|
+
return { notes, scanned, nextCursor: null };
|
|
79
|
+
if (options.maxPages && pages >= options.maxPages)
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
return { notes, scanned, nextCursor: cursor ?? null };
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Look up stored notes by commitment, chunked to the server's limit.
|
|
86
|
+
*
|
|
87
|
+
* Requests are issued concurrently. Commitments the relayer does not know are
|
|
88
|
+
* simply absent from the result — the array is not positional.
|
|
89
|
+
*/
|
|
90
|
+
export async function fetchNotesByCommitment(commitments, options = {}) {
|
|
91
|
+
const unique = [...new Set(commitments.map((c) => c.trim().toLowerCase()))];
|
|
92
|
+
if (unique.length === 0)
|
|
93
|
+
return [];
|
|
94
|
+
const chunks = [];
|
|
95
|
+
for (let i = 0; i < unique.length; i += MAX_COMMITMENTS_PER_FETCH) {
|
|
96
|
+
chunks.push(unique.slice(i, i + MAX_COMMITMENTS_PER_FETCH));
|
|
97
|
+
}
|
|
98
|
+
const pages = await Promise.all(chunks.map((chunk) => fetchNotesByCommitmentPage({
|
|
99
|
+
commitments: chunk,
|
|
100
|
+
...(options.walletPublicKey
|
|
101
|
+
? { walletPublicKey: options.walletPublicKey }
|
|
102
|
+
: {}),
|
|
103
|
+
})));
|
|
104
|
+
return pages.flatMap((page) => page.notes ?? []);
|
|
105
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Choosing which notes to spend.
|
|
3
|
+
*
|
|
4
|
+
* Two constraints drive everything here:
|
|
5
|
+
*
|
|
6
|
+
* 1. The transaction circuit is 2-in-2-out, so a single transaction can spend
|
|
7
|
+
* at most TWO notes.
|
|
8
|
+
* 2. Notes can only be co-spent if they live in the SAME Merkle tree, and the
|
|
9
|
+
* tree is derived from the MINT — see `canonicalTreeId`.
|
|
10
|
+
*/
|
|
11
|
+
/** Inputs the transaction circuit accepts in one proof. */
|
|
12
|
+
export declare const MAX_INPUT_NOTES = 2;
|
|
13
|
+
/**
|
|
14
|
+
* The tree a note belongs to, derived from its mint.
|
|
15
|
+
*
|
|
16
|
+
* Mirrors the relayer's `mintAddressToTreeId` byte for byte: the all-zero
|
|
17
|
+
* native-SOL mint maps to 0, anything else to its first four mint bytes as a
|
|
18
|
+
* big-endian uint32.
|
|
19
|
+
*
|
|
20
|
+
* DERIVE THIS, NEVER TRUST A STORED `treeId`. Change notes are written with the
|
|
21
|
+
* on-chain shard index (a small 0/1/2…) while deposits and synced notes carry
|
|
22
|
+
* the mint-derived id. For native SOL both are 0 so the difference hides; for
|
|
23
|
+
* SPL tokens the two land in different buckets, and co-spending across them
|
|
24
|
+
* fails at proof time as a commitment mismatch.
|
|
25
|
+
*
|
|
26
|
+
* An undecodable mint falls back to the SOL bucket rather than inventing a
|
|
27
|
+
* phantom tree, matching the server.
|
|
28
|
+
*/
|
|
29
|
+
export declare function canonicalTreeId(mintAddress?: string | null): number;
|
|
30
|
+
/** The minimum a note must carry to be selectable. */
|
|
31
|
+
export interface SelectableNote {
|
|
32
|
+
/** Base units. bigint preferred; strings and safe integers accepted. */
|
|
33
|
+
amount: bigint | string | number;
|
|
34
|
+
/** Mint address; determines the tree. Absent means native SOL. */
|
|
35
|
+
mint?: string | null;
|
|
36
|
+
}
|
|
37
|
+
export interface NoteSelection<T> {
|
|
38
|
+
ok: true;
|
|
39
|
+
/** The chosen notes, your own objects. */
|
|
40
|
+
notes: T[];
|
|
41
|
+
/** Tree every chosen note belongs to. */
|
|
42
|
+
treeId: number;
|
|
43
|
+
/** Sum of the chosen notes. */
|
|
44
|
+
total: bigint;
|
|
45
|
+
/** total - amount. Becomes the change note. */
|
|
46
|
+
change: bigint;
|
|
47
|
+
/**
|
|
48
|
+
* True when more than MAX_INPUT_NOTES were needed, so this cannot be proved
|
|
49
|
+
* as one transaction — the notes must be combined first.
|
|
50
|
+
*/
|
|
51
|
+
requiresMerge: boolean;
|
|
52
|
+
}
|
|
53
|
+
export interface NoteSelectionFailure {
|
|
54
|
+
ok: false;
|
|
55
|
+
reason: "NO_NOTES" | "INSUFFICIENT_FUNDS";
|
|
56
|
+
/** Largest total reachable within a single tree. */
|
|
57
|
+
available: bigint;
|
|
58
|
+
message: string;
|
|
59
|
+
}
|
|
60
|
+
export type NoteSelectionResult<T> = NoteSelection<T> | NoteSelectionFailure;
|
|
61
|
+
/**
|
|
62
|
+
* Pick notes to cover `amount`, preferring the fewest notes and least change.
|
|
63
|
+
*
|
|
64
|
+
* Considers each tree separately, since notes cannot be co-spent across trees,
|
|
65
|
+
* and returns the best single-tree selection:
|
|
66
|
+
*
|
|
67
|
+
* 1. one note that covers the amount (smallest such note, least change);
|
|
68
|
+
* 2. otherwise the best PAIR, found by a two-pointer scan over the
|
|
69
|
+
* amount-sorted notes — O(n) rather than an O(n²) all-pairs search;
|
|
70
|
+
* 3. otherwise the largest notes greedily, flagged `requiresMerge` because
|
|
71
|
+
* more than two inputs cannot go into one proof.
|
|
72
|
+
*
|
|
73
|
+
* @param amount base units to cover
|
|
74
|
+
*/
|
|
75
|
+
export declare function selectNotesForAmount<T extends SelectableNote>(notes: T[], amount: bigint | string | number, options?: {
|
|
76
|
+
mint?: string | null;
|
|
77
|
+
}): NoteSelectionResult<T>;
|
|
78
|
+
/**
|
|
79
|
+
* One input to a merge step: an existing note, or the output of an earlier step.
|
|
80
|
+
*
|
|
81
|
+
* Steps chain, so a plan for five notes refers to notes that do not exist yet.
|
|
82
|
+
* Modelling that explicitly beats returning amounts and leaving the caller to
|
|
83
|
+
* work out which is which.
|
|
84
|
+
*/
|
|
85
|
+
export type MergeInput<T> = {
|
|
86
|
+
kind: "note";
|
|
87
|
+
note: T;
|
|
88
|
+
amount: bigint;
|
|
89
|
+
} | {
|
|
90
|
+
kind: "step";
|
|
91
|
+
step: number;
|
|
92
|
+
amount: bigint;
|
|
93
|
+
};
|
|
94
|
+
export interface MergeStep<T> {
|
|
95
|
+
/** 1-based, and the number `{ kind: "step" }` inputs refer to. */
|
|
96
|
+
step: number;
|
|
97
|
+
inputs: [MergeInput<T>, MergeInput<T>];
|
|
98
|
+
/** Value of the single note this step produces. */
|
|
99
|
+
outputAmount: bigint;
|
|
100
|
+
}
|
|
101
|
+
export interface NoteConsolidationPlan<T> {
|
|
102
|
+
ok: true;
|
|
103
|
+
treeId: number;
|
|
104
|
+
/**
|
|
105
|
+
* Merges to run in order, each a 2-in-1-out self-transfer. Empty when the
|
|
106
|
+
* amount is already spendable within MAX_INPUT_NOTES.
|
|
107
|
+
*/
|
|
108
|
+
steps: MergeStep<T>[];
|
|
109
|
+
/** Notes the plan consumes, in the order it consumes them. */
|
|
110
|
+
notes: T[];
|
|
111
|
+
/** Sum of those notes. */
|
|
112
|
+
total: bigint;
|
|
113
|
+
/** total - amount, the change left after the final spend. */
|
|
114
|
+
change: bigint;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Plan how to make `amount` spendable when no one or two notes cover it.
|
|
118
|
+
*
|
|
119
|
+
* `selectNotesForAmount` reports `requiresMerge` but cannot act on it: the
|
|
120
|
+
* circuit takes two inputs, so spending five notes means merging them down
|
|
121
|
+
* first. Each step here is one 2-in-1-out transfer to yourself, and k notes
|
|
122
|
+
* need k-2 of them before a final two-input spend.
|
|
123
|
+
*
|
|
124
|
+
* Merges the two SMALLEST notes each round. That retires dust first and leaves
|
|
125
|
+
* the large notes untouched, so the plan is short and the final spend has the
|
|
126
|
+
* least change.
|
|
127
|
+
*
|
|
128
|
+
* The plan is pure — it performs nothing. Execute the steps with
|
|
129
|
+
* `privateTransfer`, feeding each step's output note into the next.
|
|
130
|
+
*/
|
|
131
|
+
export declare function planNoteConsolidation<T extends SelectableNote>(notes: T[], amount: bigint | string | number, options?: {
|
|
132
|
+
mint?: string | null;
|
|
133
|
+
}): NoteConsolidationPlan<T> | NoteSelectionFailure;
|