@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
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
|
2
|
+
import { toBaseUnits } from "./amount.js";
|
|
3
|
+
/**
|
|
4
|
+
* Choosing which notes to spend.
|
|
5
|
+
*
|
|
6
|
+
* Two constraints drive everything here:
|
|
7
|
+
*
|
|
8
|
+
* 1. The transaction circuit is 2-in-2-out, so a single transaction can spend
|
|
9
|
+
* at most TWO notes.
|
|
10
|
+
* 2. Notes can only be co-spent if they live in the SAME Merkle tree, and the
|
|
11
|
+
* tree is derived from the MINT — see `canonicalTreeId`.
|
|
12
|
+
*/
|
|
13
|
+
/** Inputs the transaction circuit accepts in one proof. */
|
|
14
|
+
export const MAX_INPUT_NOTES = 2;
|
|
15
|
+
/**
|
|
16
|
+
* The tree a note belongs to, derived from its mint.
|
|
17
|
+
*
|
|
18
|
+
* Mirrors the relayer's `mintAddressToTreeId` byte for byte: the all-zero
|
|
19
|
+
* native-SOL mint maps to 0, anything else to its first four mint bytes as a
|
|
20
|
+
* big-endian uint32.
|
|
21
|
+
*
|
|
22
|
+
* DERIVE THIS, NEVER TRUST A STORED `treeId`. Change notes are written with the
|
|
23
|
+
* on-chain shard index (a small 0/1/2…) while deposits and synced notes carry
|
|
24
|
+
* the mint-derived id. For native SOL both are 0 so the difference hides; for
|
|
25
|
+
* SPL tokens the two land in different buckets, and co-spending across them
|
|
26
|
+
* fails at proof time as a commitment mismatch.
|
|
27
|
+
*
|
|
28
|
+
* An undecodable mint falls back to the SOL bucket rather than inventing a
|
|
29
|
+
* phantom tree, matching the server.
|
|
30
|
+
*/
|
|
31
|
+
export function canonicalTreeId(mintAddress) {
|
|
32
|
+
if (!mintAddress || mintAddress.trim() === "")
|
|
33
|
+
return 0;
|
|
34
|
+
try {
|
|
35
|
+
const bytes = new PublicKey(mintAddress).toBytes();
|
|
36
|
+
if (bytes.every((byte) => byte === 0))
|
|
37
|
+
return 0;
|
|
38
|
+
return (((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]) >>> 0);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Pick notes to cover `amount`, preferring the fewest notes and least change.
|
|
46
|
+
*
|
|
47
|
+
* Considers each tree separately, since notes cannot be co-spent across trees,
|
|
48
|
+
* and returns the best single-tree selection:
|
|
49
|
+
*
|
|
50
|
+
* 1. one note that covers the amount (smallest such note, least change);
|
|
51
|
+
* 2. otherwise the best PAIR, found by a two-pointer scan over the
|
|
52
|
+
* amount-sorted notes — O(n) rather than an O(n²) all-pairs search;
|
|
53
|
+
* 3. otherwise the largest notes greedily, flagged `requiresMerge` because
|
|
54
|
+
* more than two inputs cannot go into one proof.
|
|
55
|
+
*
|
|
56
|
+
* @param amount base units to cover
|
|
57
|
+
*/
|
|
58
|
+
export function selectNotesForAmount(notes, amount, options = {}) {
|
|
59
|
+
const target = toBaseUnits(amount, "amount");
|
|
60
|
+
if (target <= 0n) {
|
|
61
|
+
throw new Error(`amount must be positive, got ${target}`);
|
|
62
|
+
}
|
|
63
|
+
const wanted = options.mint === undefined ? undefined : canonicalTreeId(options.mint);
|
|
64
|
+
const byTree = new Map();
|
|
65
|
+
notes.forEach((note, index) => {
|
|
66
|
+
const treeId = canonicalTreeId(note.mint);
|
|
67
|
+
if (wanted !== undefined && treeId !== wanted)
|
|
68
|
+
return;
|
|
69
|
+
const entry = {
|
|
70
|
+
note,
|
|
71
|
+
amount: toBaseUnits(note.amount, `notes[${index}].amount`),
|
|
72
|
+
};
|
|
73
|
+
if (entry.amount <= 0n)
|
|
74
|
+
return;
|
|
75
|
+
const bucket = byTree.get(treeId);
|
|
76
|
+
if (bucket)
|
|
77
|
+
bucket.push(entry);
|
|
78
|
+
else
|
|
79
|
+
byTree.set(treeId, [entry]);
|
|
80
|
+
});
|
|
81
|
+
if (byTree.size === 0) {
|
|
82
|
+
return {
|
|
83
|
+
ok: false,
|
|
84
|
+
reason: "NO_NOTES",
|
|
85
|
+
available: 0n,
|
|
86
|
+
message: "No spendable notes for the requested mint.",
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
let best;
|
|
90
|
+
let available = 0n;
|
|
91
|
+
for (const [treeId, bucket] of byTree) {
|
|
92
|
+
const treeTotal = bucket.reduce((sum, entry) => sum + entry.amount, 0n);
|
|
93
|
+
if (treeTotal > available)
|
|
94
|
+
available = treeTotal;
|
|
95
|
+
const selection = selectWithinTree(bucket, target, treeId);
|
|
96
|
+
if (!selection)
|
|
97
|
+
continue;
|
|
98
|
+
if (!best || isBetter(selection, best))
|
|
99
|
+
best = selection;
|
|
100
|
+
}
|
|
101
|
+
if (!best) {
|
|
102
|
+
return {
|
|
103
|
+
ok: false,
|
|
104
|
+
reason: "INSUFFICIENT_FUNDS",
|
|
105
|
+
available,
|
|
106
|
+
message: `No single tree holds ${target} base units. Largest tree total is ` +
|
|
107
|
+
`${available}. Notes in different trees cannot be co-spent.`,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return best;
|
|
111
|
+
}
|
|
112
|
+
/** Fewer inputs first, then least change. */
|
|
113
|
+
function isBetter(a, b) {
|
|
114
|
+
if (a.notes.length !== b.notes.length)
|
|
115
|
+
return a.notes.length < b.notes.length;
|
|
116
|
+
return a.change < b.change;
|
|
117
|
+
}
|
|
118
|
+
function selectWithinTree(bucket, target, treeId) {
|
|
119
|
+
// Descending by amount, compared as bigint: Number() would lose precision
|
|
120
|
+
// above 2^53 base units.
|
|
121
|
+
const sorted = [...bucket].sort((a, b) => b.amount > a.amount ? 1 : b.amount < a.amount ? -1 : 0);
|
|
122
|
+
// 1. Smallest single note that covers the target — walk up from the end.
|
|
123
|
+
let single;
|
|
124
|
+
for (let i = sorted.length - 1; i >= 0; i--) {
|
|
125
|
+
if (sorted[i].amount >= target) {
|
|
126
|
+
single = sorted[i];
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (single) {
|
|
131
|
+
return {
|
|
132
|
+
ok: true,
|
|
133
|
+
notes: [single.note],
|
|
134
|
+
treeId,
|
|
135
|
+
total: single.amount,
|
|
136
|
+
change: single.amount - target,
|
|
137
|
+
requiresMerge: false,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
// 2. Best pair by two-pointer scan over the descending list.
|
|
141
|
+
let bestPair;
|
|
142
|
+
let bestChange = 0n;
|
|
143
|
+
let lo = 0;
|
|
144
|
+
let hi = sorted.length - 1;
|
|
145
|
+
while (lo < hi) {
|
|
146
|
+
const combined = sorted[lo].amount + sorted[hi].amount;
|
|
147
|
+
if (combined >= target) {
|
|
148
|
+
const change = combined - target;
|
|
149
|
+
if (!bestPair || change < bestChange) {
|
|
150
|
+
bestChange = change;
|
|
151
|
+
bestPair = [sorted[lo], sorted[hi]];
|
|
152
|
+
}
|
|
153
|
+
lo++;
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
hi--;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (bestPair) {
|
|
160
|
+
return {
|
|
161
|
+
ok: true,
|
|
162
|
+
notes: [bestPair[0].note, bestPair[1].note],
|
|
163
|
+
treeId,
|
|
164
|
+
total: bestPair[0].amount + bestPair[1].amount,
|
|
165
|
+
change: bestChange,
|
|
166
|
+
requiresMerge: false,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
// 3. Greedy over the largest notes. More than two inputs, so it needs a merge.
|
|
170
|
+
const chosen = [];
|
|
171
|
+
let total = 0n;
|
|
172
|
+
for (const entry of sorted) {
|
|
173
|
+
chosen.push(entry);
|
|
174
|
+
total += entry.amount;
|
|
175
|
+
if (total >= target)
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
if (total < target)
|
|
179
|
+
return undefined;
|
|
180
|
+
return {
|
|
181
|
+
ok: true,
|
|
182
|
+
notes: chosen.map((entry) => entry.note),
|
|
183
|
+
treeId,
|
|
184
|
+
total,
|
|
185
|
+
change: total - target,
|
|
186
|
+
requiresMerge: chosen.length > MAX_INPUT_NOTES,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Plan how to make `amount` spendable when no one or two notes cover it.
|
|
191
|
+
*
|
|
192
|
+
* `selectNotesForAmount` reports `requiresMerge` but cannot act on it: the
|
|
193
|
+
* circuit takes two inputs, so spending five notes means merging them down
|
|
194
|
+
* first. Each step here is one 2-in-1-out transfer to yourself, and k notes
|
|
195
|
+
* need k-2 of them before a final two-input spend.
|
|
196
|
+
*
|
|
197
|
+
* Merges the two SMALLEST notes each round. That retires dust first and leaves
|
|
198
|
+
* the large notes untouched, so the plan is short and the final spend has the
|
|
199
|
+
* least change.
|
|
200
|
+
*
|
|
201
|
+
* The plan is pure — it performs nothing. Execute the steps with
|
|
202
|
+
* `privateTransfer`, feeding each step's output note into the next.
|
|
203
|
+
*/
|
|
204
|
+
export function planNoteConsolidation(notes, amount, options = {}) {
|
|
205
|
+
const target = toBaseUnits(amount, "amount");
|
|
206
|
+
if (target <= 0n) {
|
|
207
|
+
throw new Error(`amount must be positive, got ${target}`);
|
|
208
|
+
}
|
|
209
|
+
const selection = selectNotesForAmount(notes, target, options);
|
|
210
|
+
if (!selection.ok)
|
|
211
|
+
return selection;
|
|
212
|
+
// Already spendable in one transaction.
|
|
213
|
+
if (!selection.requiresMerge) {
|
|
214
|
+
return {
|
|
215
|
+
ok: true,
|
|
216
|
+
treeId: selection.treeId,
|
|
217
|
+
steps: [],
|
|
218
|
+
notes: selection.notes,
|
|
219
|
+
total: selection.total,
|
|
220
|
+
change: selection.change,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
// Ascending, so the two cheapest merge first.
|
|
224
|
+
const pool = selection.notes
|
|
225
|
+
.map((note, index) => ({
|
|
226
|
+
kind: "note",
|
|
227
|
+
note,
|
|
228
|
+
amount: toBaseUnits(note.amount, `notes[${index}].amount`),
|
|
229
|
+
}))
|
|
230
|
+
.sort((a, b) => (a.amount > b.amount ? 1 : a.amount < b.amount ? -1 : 0));
|
|
231
|
+
const steps = [];
|
|
232
|
+
while (pool.length > MAX_INPUT_NOTES) {
|
|
233
|
+
const first = pool.shift();
|
|
234
|
+
const second = pool.shift();
|
|
235
|
+
const outputAmount = first.amount + second.amount;
|
|
236
|
+
const step = steps.length + 1;
|
|
237
|
+
steps.push({ step, inputs: [first, second], outputAmount });
|
|
238
|
+
const produced = { kind: "step", step, amount: outputAmount };
|
|
239
|
+
// Keep the pool ordered so the next round still merges the two smallest.
|
|
240
|
+
const at = pool.findIndex((entry) => entry.amount > outputAmount);
|
|
241
|
+
if (at === -1)
|
|
242
|
+
pool.push(produced);
|
|
243
|
+
else
|
|
244
|
+
pool.splice(at, 0, produced);
|
|
245
|
+
}
|
|
246
|
+
return {
|
|
247
|
+
ok: true,
|
|
248
|
+
treeId: selection.treeId,
|
|
249
|
+
steps,
|
|
250
|
+
notes: selection.notes,
|
|
251
|
+
total: selection.total,
|
|
252
|
+
change: selection.change,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spend status and private balance.
|
|
3
|
+
*
|
|
4
|
+
* A note is yours until its nullifier is published; nothing in the note itself
|
|
5
|
+
* says whether it has been spent. So a balance is always two steps: ask which
|
|
6
|
+
* nullifiers are spent, then sum what is left. This module is that pair.
|
|
7
|
+
*/
|
|
8
|
+
/** Server-side cap per check-batch request (notes.constants MAX_NULLIFIERS_PER_CHECK). */
|
|
9
|
+
export declare const MAX_NULLIFIERS_PER_CHECK = 200;
|
|
10
|
+
/** Grouping key used for notes that carry no mint. */
|
|
11
|
+
export declare const UNKNOWN_MINT = "unknown";
|
|
12
|
+
/**
|
|
13
|
+
* Ask the relayer which of these nullifiers are spent.
|
|
14
|
+
*
|
|
15
|
+
* Deduplicates, lower-cases, and splits into MAX_NULLIFIERS_PER_CHECK batches
|
|
16
|
+
* that are issued concurrently. Returns a Set for membership testing; a
|
|
17
|
+
* nullifier absent from it is unspent as far as the relayer knows.
|
|
18
|
+
*
|
|
19
|
+
* @throws if any entry is not 64-char hex — a malformed nullifier that silently
|
|
20
|
+
* read as "unspent" would overstate a balance.
|
|
21
|
+
*/
|
|
22
|
+
export declare function checkNullifiersSpent(nullifiers: string[]): Promise<Set<string>>;
|
|
23
|
+
/** The minimum a note must carry to be counted. */
|
|
24
|
+
export interface SpendableNote {
|
|
25
|
+
/** Base units. bigint preferred; strings and safe integers are accepted. */
|
|
26
|
+
amount: bigint | string | number;
|
|
27
|
+
/** 64-character hex nullifier for this note. */
|
|
28
|
+
nullifier: string;
|
|
29
|
+
/** Mint this note is denominated in. Groups the result; optional. */
|
|
30
|
+
mint?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface PrivateBalance<T extends SpendableNote = SpendableNote> {
|
|
33
|
+
/** Sum of every unspent note, across all mints. */
|
|
34
|
+
total: bigint;
|
|
35
|
+
/** Unspent totals keyed by mint (UNKNOWN_MINT when a note carries none). */
|
|
36
|
+
byMint: Record<string, bigint>;
|
|
37
|
+
/** The caller's own note objects, partitioned. */
|
|
38
|
+
unspent: T[];
|
|
39
|
+
spent: T[];
|
|
40
|
+
}
|
|
41
|
+
export interface PrivateBalanceOptions {
|
|
42
|
+
/**
|
|
43
|
+
* Supply spend status yourself instead of asking the relayer — e.g. from a
|
|
44
|
+
* chain scan of nullifier events, or a local cache. Receives normalized
|
|
45
|
+
* lower-case hex.
|
|
46
|
+
*/
|
|
47
|
+
resolveSpent?: (nullifiers: string[]) => Promise<Set<string>> | Set<string>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Sum unspent notes, per mint and in total.
|
|
51
|
+
*
|
|
52
|
+
* ```ts
|
|
53
|
+
* const { total, byMint, unspent } = await getPrivateBalance(myNotes);
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* Amounts are base units and stay bigint throughout — never sum note amounts as
|
|
57
|
+
* numbers. Notes are matched to spend status by nullifier, so every note needs
|
|
58
|
+
* one; the partitioned arrays hand back your own objects, not copies.
|
|
59
|
+
*/
|
|
60
|
+
export declare function getPrivateBalance<T extends SpendableNote>(notes: T[], options?: PrivateBalanceOptions): Promise<PrivateBalance<T>>;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { checkNullifiers } from "../relayer/api.js";
|
|
2
|
+
import { toBaseUnits } from "./amount.js";
|
|
3
|
+
/**
|
|
4
|
+
* Spend status and private balance.
|
|
5
|
+
*
|
|
6
|
+
* A note is yours until its nullifier is published; nothing in the note itself
|
|
7
|
+
* says whether it has been spent. So a balance is always two steps: ask which
|
|
8
|
+
* nullifiers are spent, then sum what is left. This module is that pair.
|
|
9
|
+
*/
|
|
10
|
+
/** Server-side cap per check-batch request (notes.constants MAX_NULLIFIERS_PER_CHECK). */
|
|
11
|
+
export const MAX_NULLIFIERS_PER_CHECK = 200;
|
|
12
|
+
/** Grouping key used for notes that carry no mint. */
|
|
13
|
+
export const UNKNOWN_MINT = "unknown";
|
|
14
|
+
const HEX64 = /^[0-9a-f]{64}$/;
|
|
15
|
+
/**
|
|
16
|
+
* Normalize a nullifier to the form the relayer answers in.
|
|
17
|
+
*
|
|
18
|
+
* The endpoint's validator is case-INSENSITIVE, but it answers with
|
|
19
|
+
* `Buffer.toString("hex")` — always lowercase. Sending upper-case hex therefore
|
|
20
|
+
* returns a `spent` list that never matches what you sent, and every note reads
|
|
21
|
+
* as unspent. Normalizing here is what makes membership testing sound.
|
|
22
|
+
*/
|
|
23
|
+
function normalizeNullifier(nullifier, index) {
|
|
24
|
+
const normalized = nullifier.trim().toLowerCase();
|
|
25
|
+
if (!HEX64.test(normalized)) {
|
|
26
|
+
throw new Error(`nullifiers[${index}] must be a 64-character hex string, got ` +
|
|
27
|
+
`${JSON.stringify(nullifier)}`);
|
|
28
|
+
}
|
|
29
|
+
return normalized;
|
|
30
|
+
}
|
|
31
|
+
function chunk(items, size) {
|
|
32
|
+
const out = [];
|
|
33
|
+
for (let i = 0; i < items.length; i += size)
|
|
34
|
+
out.push(items.slice(i, i + size));
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Ask the relayer which of these nullifiers are spent.
|
|
39
|
+
*
|
|
40
|
+
* Deduplicates, lower-cases, and splits into MAX_NULLIFIERS_PER_CHECK batches
|
|
41
|
+
* that are issued concurrently. Returns a Set for membership testing; a
|
|
42
|
+
* nullifier absent from it is unspent as far as the relayer knows.
|
|
43
|
+
*
|
|
44
|
+
* @throws if any entry is not 64-char hex — a malformed nullifier that silently
|
|
45
|
+
* read as "unspent" would overstate a balance.
|
|
46
|
+
*/
|
|
47
|
+
export async function checkNullifiersSpent(nullifiers) {
|
|
48
|
+
const unique = [...new Set(nullifiers.map(normalizeNullifier))];
|
|
49
|
+
if (unique.length === 0)
|
|
50
|
+
return new Set();
|
|
51
|
+
const batches = await Promise.all(chunk(unique, MAX_NULLIFIERS_PER_CHECK).map((batch) => checkNullifiers(batch)));
|
|
52
|
+
const spent = new Set();
|
|
53
|
+
for (const response of batches) {
|
|
54
|
+
for (const nullifier of response.spent ?? []) {
|
|
55
|
+
spent.add(nullifier.toLowerCase());
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return spent;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Sum unspent notes, per mint and in total.
|
|
62
|
+
*
|
|
63
|
+
* ```ts
|
|
64
|
+
* const { total, byMint, unspent } = await getPrivateBalance(myNotes);
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* Amounts are base units and stay bigint throughout — never sum note amounts as
|
|
68
|
+
* numbers. Notes are matched to spend status by nullifier, so every note needs
|
|
69
|
+
* one; the partitioned arrays hand back your own objects, not copies.
|
|
70
|
+
*/
|
|
71
|
+
export async function getPrivateBalance(notes, options = {}) {
|
|
72
|
+
const normalized = notes.map((note, index) => ({
|
|
73
|
+
note,
|
|
74
|
+
amount: toBaseUnits(note.amount, `notes[${index}].amount`),
|
|
75
|
+
nullifier: normalizeNullifier(note.nullifier, index),
|
|
76
|
+
}));
|
|
77
|
+
const resolve = options.resolveSpent ?? checkNullifiersSpent;
|
|
78
|
+
const spentSet = await resolve(normalized.map((entry) => entry.nullifier));
|
|
79
|
+
const balance = {
|
|
80
|
+
total: 0n,
|
|
81
|
+
byMint: {},
|
|
82
|
+
unspent: [],
|
|
83
|
+
spent: [],
|
|
84
|
+
};
|
|
85
|
+
for (const entry of normalized) {
|
|
86
|
+
if (spentSet.has(entry.nullifier)) {
|
|
87
|
+
balance.spent.push(entry.note);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const mint = entry.note.mint ?? UNKNOWN_MINT;
|
|
91
|
+
balance.unspent.push(entry.note);
|
|
92
|
+
balance.total += entry.amount;
|
|
93
|
+
balance.byMint[mint] = (balance.byMint[mint] ?? 0n) + entry.amount;
|
|
94
|
+
}
|
|
95
|
+
return balance;
|
|
96
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { JperpMarket, JperpMarketPayload, JperpSide } from "./types.js";
|
|
2
|
+
export declare const JUPITER_PERPS_PROGRAM_ID = "PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu";
|
|
3
|
+
export declare const JUPITER_PERPS_EVENT_AUTHORITY = "37hJBDnntwqhGbK7L6M1bLyvccj4u55CCUiLPdYkiqBN";
|
|
4
|
+
export declare const JUPITER_PERPS_POOL = "5BUwFW4nRbftYTDMbgxykoFWqWHPzahFSNAaaaJtVKsq";
|
|
5
|
+
export declare const JUPITER_JLP_MINT = "27G8MtK7VtTcCHkpASjSDdkWWYfoqT6ggEuKidVJidD4";
|
|
6
|
+
export declare const JPERP_USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
7
|
+
export declare const JPERP_NATIVE_SOL_MINT = "11111111111111111111111111111111";
|
|
8
|
+
export declare const JPERP_WRAPPED_SOL_MINT = "So11111111111111111111111111111111111111112";
|
|
9
|
+
export declare const JUPITER_PERPS_CUSTODIES: Record<JperpMarket | "USDC" | "USDT", string>;
|
|
10
|
+
export declare const JUPITER_PERPS_DOVES_ORACLES: Record<JperpMarket, string>;
|
|
11
|
+
export declare const JPERP_LONG_POOL_MINTS: Record<JperpMarket, string>;
|
|
12
|
+
export declare const JPERP_LONG_JUPITER_MINTS: Record<JperpMarket, string>;
|
|
13
|
+
export declare const JPERP_LONG_DECIMALS: Record<JperpMarket, number>;
|
|
14
|
+
export declare const JPERP_USD_DECIMALS = 6;
|
|
15
|
+
/** Resolve the exact market payload duplicated by the wallet-app reference. */
|
|
16
|
+
export declare function getJperpMarketPayload(market: JperpMarket, side: JperpSide): JperpMarketPayload;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export const JUPITER_PERPS_PROGRAM_ID = "PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu";
|
|
2
|
+
export const JUPITER_PERPS_EVENT_AUTHORITY = "37hJBDnntwqhGbK7L6M1bLyvccj4u55CCUiLPdYkiqBN";
|
|
3
|
+
export const JUPITER_PERPS_POOL = "5BUwFW4nRbftYTDMbgxykoFWqWHPzahFSNAaaaJtVKsq";
|
|
4
|
+
export const JUPITER_JLP_MINT = "27G8MtK7VtTcCHkpASjSDdkWWYfoqT6ggEuKidVJidD4";
|
|
5
|
+
export const JPERP_USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
6
|
+
export const JPERP_NATIVE_SOL_MINT = "11111111111111111111111111111111";
|
|
7
|
+
export const JPERP_WRAPPED_SOL_MINT = "So11111111111111111111111111111111111111112";
|
|
8
|
+
export const JUPITER_PERPS_CUSTODIES = {
|
|
9
|
+
SOL: "7xS2gz2bTp3fwCC7knJvUWTEU9Tycczu6VhJYKgi1wdz",
|
|
10
|
+
ETH: "AQCGyheWPLeo6Qp9WpYS9m3Qj479t7R636N9ey1rEjEn",
|
|
11
|
+
BTC: "5Pv3gM9JrFFH883SWAhvJC9RPYmo8UNxuFtv5bMMALkm",
|
|
12
|
+
USDC: "G18jKKXQwBbrHeiK3C9MRXhkHsLHf7XgCSisykV46EZa",
|
|
13
|
+
USDT: "4vkNeXiYEUizLdrpdPS1eC2mccyM4NUPRtERrk6ZETkk",
|
|
14
|
+
};
|
|
15
|
+
export const JUPITER_PERPS_DOVES_ORACLES = {
|
|
16
|
+
SOL: "FYq2BWQ1V5P1WFBqr3qB2Kb5yHVvSv7upzKodgQE5zXh",
|
|
17
|
+
ETH: "AFZnHPzy4mvVCffrVwhewHbFc93uTHvDSFrVH7GtfXF1",
|
|
18
|
+
BTC: "hUqAT1KQ7eW1i6Csp9CXYtpPfSAvi835V7wKi5fRfmC",
|
|
19
|
+
};
|
|
20
|
+
export const JPERP_LONG_POOL_MINTS = {
|
|
21
|
+
SOL: JPERP_NATIVE_SOL_MINT,
|
|
22
|
+
ETH: "7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs",
|
|
23
|
+
BTC: "3NZ9JMVBmGAqocybic2c7LQCJScmgsAZ6vQqTDzcqmJh",
|
|
24
|
+
};
|
|
25
|
+
export const JPERP_LONG_JUPITER_MINTS = {
|
|
26
|
+
SOL: JPERP_WRAPPED_SOL_MINT,
|
|
27
|
+
ETH: JPERP_LONG_POOL_MINTS.ETH,
|
|
28
|
+
BTC: JPERP_LONG_POOL_MINTS.BTC,
|
|
29
|
+
};
|
|
30
|
+
export const JPERP_LONG_DECIMALS = {
|
|
31
|
+
SOL: 9,
|
|
32
|
+
ETH: 8,
|
|
33
|
+
BTC: 8,
|
|
34
|
+
};
|
|
35
|
+
export const JPERP_USD_DECIMALS = 6;
|
|
36
|
+
/** Resolve the exact market payload duplicated by the wallet-app reference. */
|
|
37
|
+
export function getJperpMarketPayload(market, side) {
|
|
38
|
+
const isLong = side === "long";
|
|
39
|
+
return {
|
|
40
|
+
custody: JUPITER_PERPS_CUSTODIES[market],
|
|
41
|
+
collateralCustody: JUPITER_PERPS_CUSTODIES[isLong ? market : "USDC"],
|
|
42
|
+
inputMint: isLong ? JPERP_LONG_POOL_MINTS[market] : JPERP_USDC_MINT,
|
|
43
|
+
jupiterInputMint: isLong
|
|
44
|
+
? JPERP_LONG_JUPITER_MINTS[market]
|
|
45
|
+
: JPERP_USDC_MINT,
|
|
46
|
+
dovesOracle: JUPITER_PERPS_DOVES_ORACLES[market],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Keypair } from "@solana/web3.js";
|
|
2
|
+
export interface JperpKeyBundle {
|
|
3
|
+
withdrawalId: Uint8Array;
|
|
4
|
+
withdrawalIdHex: string;
|
|
5
|
+
claimant: Keypair;
|
|
6
|
+
claimantPublicKey: string;
|
|
7
|
+
claimantSecretKey: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function deriveJperpWithdrawalId(spendingKey: Uint8Array, index: number): Uint8Array;
|
|
10
|
+
export declare function deriveJperpClaimantKeypair(spendingKey: Uint8Array, withdrawalId: Uint8Array): Keypair;
|
|
11
|
+
export declare function jperpWithdrawalIdToHex(withdrawalId: Uint8Array): string;
|
|
12
|
+
export declare function jperpWithdrawalIdFromHex(value: string): Uint8Array;
|
|
13
|
+
/** Derive every client-held key field needed across the jPerp lifecycle. */
|
|
14
|
+
export declare function deriveJperpKeyBundle(spendingKey: Uint8Array, index: number): JperpKeyBundle;
|
|
15
|
+
/** Match the client reference's per-request Jupiter PDA namespace. */
|
|
16
|
+
export declare function createJperpCounter(): string;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { sha256 } from "@noble/hashes/sha256";
|
|
2
|
+
import { Keypair } from "@solana/web3.js";
|
|
3
|
+
import { assertBytes, bytesToHex, concatBytes, encodeBase58, hexToBytes, uint32Le, utf8, } from "../identity/encoding.js";
|
|
4
|
+
export function deriveJperpWithdrawalId(spendingKey, index) {
|
|
5
|
+
assertBytes(spendingKey, 32, "spending key");
|
|
6
|
+
return sha256(concatBytes(utf8("jperp_withdrawal_v1"), spendingKey, uint32Le(index, "jPerp position index")));
|
|
7
|
+
}
|
|
8
|
+
export function deriveJperpClaimantKeypair(spendingKey, withdrawalId) {
|
|
9
|
+
assertBytes(spendingKey, 32, "spending key");
|
|
10
|
+
assertBytes(withdrawalId, 32, "jPerp withdrawal ID");
|
|
11
|
+
return Keypair.fromSeed(sha256(concatBytes(utf8("jperp_claimant"), spendingKey, withdrawalId)));
|
|
12
|
+
}
|
|
13
|
+
export function jperpWithdrawalIdToHex(withdrawalId) {
|
|
14
|
+
assertBytes(withdrawalId, 32, "jPerp withdrawal ID");
|
|
15
|
+
return bytesToHex(withdrawalId);
|
|
16
|
+
}
|
|
17
|
+
export function jperpWithdrawalIdFromHex(value) {
|
|
18
|
+
return hexToBytes(value, 32, "jPerp withdrawal ID");
|
|
19
|
+
}
|
|
20
|
+
/** Derive every client-held key field needed across the jPerp lifecycle. */
|
|
21
|
+
export function deriveJperpKeyBundle(spendingKey, index) {
|
|
22
|
+
const withdrawalId = deriveJperpWithdrawalId(spendingKey, index);
|
|
23
|
+
const claimant = deriveJperpClaimantKeypair(spendingKey, withdrawalId);
|
|
24
|
+
return {
|
|
25
|
+
withdrawalId,
|
|
26
|
+
withdrawalIdHex: jperpWithdrawalIdToHex(withdrawalId),
|
|
27
|
+
claimant,
|
|
28
|
+
claimantPublicKey: claimant.publicKey.toBase58(),
|
|
29
|
+
claimantSecretKey: encodeBase58(claimant.secretKey),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** Match the client reference's per-request Jupiter PDA namespace. */
|
|
33
|
+
export function createJperpCounter() {
|
|
34
|
+
return String(Math.floor(Math.random() * 1000000000000));
|
|
35
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
|
2
|
+
import type { JperpSide } from "./types.js";
|
|
3
|
+
export declare const JPERP_SIDE_LONG = 1;
|
|
4
|
+
export declare const JPERP_SIDE_SHORT = 2;
|
|
5
|
+
export declare const JPERP_CHANGE_INCREASE = 1;
|
|
6
|
+
export declare const JPERP_CHANGE_DECREASE = 2;
|
|
7
|
+
export declare function deriveJupiterPerpetualsPda(): PublicKey;
|
|
8
|
+
export declare function deriveJperpExecutorPda(params: {
|
|
9
|
+
programId: PublicKey;
|
|
10
|
+
mint: PublicKey;
|
|
11
|
+
claimant: PublicKey;
|
|
12
|
+
withdrawalId: Uint8Array;
|
|
13
|
+
}): PublicKey;
|
|
14
|
+
export declare function deriveJperpSlotPda(params: {
|
|
15
|
+
programId: PublicKey;
|
|
16
|
+
mint: PublicKey;
|
|
17
|
+
withdrawalId: Uint8Array;
|
|
18
|
+
}): PublicKey;
|
|
19
|
+
export declare function deriveJupiterPerpPositionPda(params: {
|
|
20
|
+
executor: PublicKey;
|
|
21
|
+
custody: PublicKey;
|
|
22
|
+
collateralCustody: PublicKey;
|
|
23
|
+
side: JperpSide;
|
|
24
|
+
}): PublicKey;
|
|
25
|
+
export declare function deriveJupiterPerpRequestPda(params: {
|
|
26
|
+
position: PublicKey;
|
|
27
|
+
counter: bigint;
|
|
28
|
+
change: "increase" | "decrease";
|
|
29
|
+
}): PublicKey;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
|
2
|
+
import { assertBytes, uint64Le, utf8, } from "../identity/encoding.js";
|
|
3
|
+
import { JUPITER_PERPS_POOL, JUPITER_PERPS_PROGRAM_ID, } from "./constants.js";
|
|
4
|
+
export const JPERP_SIDE_LONG = 1;
|
|
5
|
+
export const JPERP_SIDE_SHORT = 2;
|
|
6
|
+
export const JPERP_CHANGE_INCREASE = 1;
|
|
7
|
+
export const JPERP_CHANGE_DECREASE = 2;
|
|
8
|
+
export function deriveJupiterPerpetualsPda() {
|
|
9
|
+
return PublicKey.findProgramAddressSync([utf8("perpetuals")], new PublicKey(JUPITER_PERPS_PROGRAM_ID))[0];
|
|
10
|
+
}
|
|
11
|
+
export function deriveJperpExecutorPda(params) {
|
|
12
|
+
assertBytes(params.withdrawalId, 32, "jPerp withdrawal ID");
|
|
13
|
+
return PublicKey.findProgramAddressSync([
|
|
14
|
+
utf8("jperp_executor"),
|
|
15
|
+
params.mint.toBytes(),
|
|
16
|
+
params.claimant.toBytes(),
|
|
17
|
+
params.withdrawalId,
|
|
18
|
+
], params.programId)[0];
|
|
19
|
+
}
|
|
20
|
+
export function deriveJperpSlotPda(params) {
|
|
21
|
+
assertBytes(params.withdrawalId, 32, "jPerp withdrawal ID");
|
|
22
|
+
return PublicKey.findProgramAddressSync([utf8("jperp_slot_v1"), params.mint.toBytes(), params.withdrawalId], params.programId)[0];
|
|
23
|
+
}
|
|
24
|
+
export function deriveJupiterPerpPositionPda(params) {
|
|
25
|
+
return PublicKey.findProgramAddressSync([
|
|
26
|
+
utf8("position"),
|
|
27
|
+
params.executor.toBytes(),
|
|
28
|
+
new PublicKey(JUPITER_PERPS_POOL).toBytes(),
|
|
29
|
+
params.custody.toBytes(),
|
|
30
|
+
params.collateralCustody.toBytes(),
|
|
31
|
+
Uint8Array.from([
|
|
32
|
+
params.side === "long" ? JPERP_SIDE_LONG : JPERP_SIDE_SHORT,
|
|
33
|
+
]),
|
|
34
|
+
], new PublicKey(JUPITER_PERPS_PROGRAM_ID))[0];
|
|
35
|
+
}
|
|
36
|
+
export function deriveJupiterPerpRequestPda(params) {
|
|
37
|
+
return PublicKey.findProgramAddressSync([
|
|
38
|
+
utf8("position_request"),
|
|
39
|
+
params.position.toBytes(),
|
|
40
|
+
uint64Le(params.counter, "jPerp request counter"),
|
|
41
|
+
Uint8Array.from([
|
|
42
|
+
params.change === "increase"
|
|
43
|
+
? JPERP_CHANGE_INCREASE
|
|
44
|
+
: JPERP_CHANGE_DECREASE,
|
|
45
|
+
]),
|
|
46
|
+
], new PublicKey(JUPITER_PERPS_PROGRAM_ID))[0];
|
|
47
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { JperpCancelTriggerRequest, JperpCancelTriggerResponse, JperpCloseRequest, JperpDecreaseResponse, JperpOpenRequest, JperpOpenResponse, JperpRecoverNativeRequest, JperpRecoverNativeResponse, JperpRecoverSeedRequest, JperpRecoverSeedResponse, JperpReissueRequest, JperpReissueResponse, JperpSetTpslRequest, JperpUpdateTpslRequest } from "./types.js";
|
|
2
|
+
export declare function submitJperpOpen(data: JperpOpenRequest): Promise<JperpOpenResponse>;
|
|
3
|
+
export declare function submitJperpClose(data: JperpCloseRequest): Promise<JperpDecreaseResponse>;
|
|
4
|
+
export declare function submitJperpSetTpsl(data: JperpSetTpslRequest): Promise<JperpDecreaseResponse>;
|
|
5
|
+
export declare function submitJperpUpdateTpsl(data: JperpUpdateTpslRequest): Promise<JperpDecreaseResponse>;
|
|
6
|
+
export declare function submitJperpCancelTrigger(data: JperpCancelTriggerRequest): Promise<JperpCancelTriggerResponse>;
|
|
7
|
+
export declare function submitJperpReissue(data: JperpReissueRequest): Promise<JperpReissueResponse>;
|
|
8
|
+
export declare function submitJperpRecoverSeed(data: JperpRecoverSeedRequest): Promise<JperpRecoverSeedResponse>;
|
|
9
|
+
export declare function submitJperpRecoverNative(data: JperpRecoverNativeRequest): Promise<JperpRecoverNativeResponse>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { getInternalRelayerClient } from "../relayer/internal.js";
|
|
2
|
+
export function submitJperpOpen(data) {
|
|
3
|
+
return getInternalRelayerClient().submitJperpOpen(data);
|
|
4
|
+
}
|
|
5
|
+
export function submitJperpClose(data) {
|
|
6
|
+
return getInternalRelayerClient().submitJperpClose(data);
|
|
7
|
+
}
|
|
8
|
+
export function submitJperpSetTpsl(data) {
|
|
9
|
+
return getInternalRelayerClient().submitJperpSetTpsl(data);
|
|
10
|
+
}
|
|
11
|
+
export function submitJperpUpdateTpsl(data) {
|
|
12
|
+
return getInternalRelayerClient().submitJperpUpdateTpsl(data);
|
|
13
|
+
}
|
|
14
|
+
export function submitJperpCancelTrigger(data) {
|
|
15
|
+
return getInternalRelayerClient().submitJperpCancelTrigger(data);
|
|
16
|
+
}
|
|
17
|
+
export function submitJperpReissue(data) {
|
|
18
|
+
return getInternalRelayerClient().submitJperpReissue(data);
|
|
19
|
+
}
|
|
20
|
+
export function submitJperpRecoverSeed(data) {
|
|
21
|
+
return getInternalRelayerClient().submitJperpRecoverSeed(data);
|
|
22
|
+
}
|
|
23
|
+
export function submitJperpRecoverNative(data) {
|
|
24
|
+
return getInternalRelayerClient().submitJperpRecoverNative(data);
|
|
25
|
+
}
|