@voidifydao/sdk 1.0.0 → 2.0.1

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.
Files changed (59) hide show
  1. package/README.md +0 -0
  2. package/dist/cli/config/init.js +1 -7
  3. package/dist/cli/config/loader.js +0 -1
  4. package/dist/cli/config/types.d.ts +0 -1
  5. package/dist/cli/deposit.js +45 -29
  6. package/dist/cli/helpers.js +0 -3
  7. package/dist/cli/progress.d.ts +5 -0
  8. package/dist/cli/progress.js +37 -0
  9. package/dist/cli/relayer.js +16 -7
  10. package/dist/cli/withdraw.js +20 -6
  11. package/dist/context.d.ts +1 -4
  12. package/dist/context.js +14 -12
  13. package/dist/idl/voidify/idl.d.ts +1 -1
  14. package/dist/idl/voidify/idl.json +1 -1
  15. package/dist/index.d.ts +2 -3
  16. package/dist/index.js +0 -2
  17. package/dist/relayer/server/server.js +29 -13
  18. package/dist/relayer/server/switchboard.js +15 -6
  19. package/dist/relayer/types.d.ts +1 -0
  20. package/dist/substream/chain/index.d.ts +10 -3
  21. package/dist/substream/chain/index.js +14 -7
  22. package/dist/substream/chain/registry.d.ts +2 -2
  23. package/dist/substream/chain/utils.d.ts +2 -1
  24. package/dist/substream/chain/utils.js +35 -11
  25. package/dist/substream/client.d.ts +6 -1
  26. package/dist/substream/database/indexeddb.js +3 -0
  27. package/dist/substream/database/sqlite.d.ts +1 -0
  28. package/dist/substream/database/sqlite.js +6 -0
  29. package/dist/substream/modules/deposit.d.ts +2 -1
  30. package/dist/substream/modules/deposit.js +24 -20
  31. package/dist/substream/modules/relayer.d.ts +2 -1
  32. package/dist/substream/modules/relayer.js +39 -33
  33. package/dist/substream/runtime.d.ts +19 -4
  34. package/dist/substream/runtime.js +216 -16
  35. package/dist/substream/server/server.d.ts +2 -0
  36. package/dist/substream/server/server.js +42 -8
  37. package/dist/substream/types.d.ts +21 -0
  38. package/dist/types/index.d.ts +0 -1
  39. package/dist/types/index.js +1 -1
  40. package/dist/voidify/deposit.d.ts +2 -0
  41. package/dist/voidify/deposit.js +1 -1
  42. package/dist/voidify/program.d.ts +0 -4
  43. package/dist/voidify/program.js +0 -10
  44. package/dist/voidify/relayer/list.d.ts +2 -0
  45. package/dist/voidify/relayer/list.js +1 -1
  46. package/dist/voidify/withdraw.d.ts +7 -2
  47. package/dist/voidify/withdraw.js +68 -10
  48. package/package.json +5 -4
  49. package/dist/idl/voidify-staking/idl.d.ts +0 -93
  50. package/dist/idl/voidify-staking/idl.js +0 -1
  51. package/dist/idl/voidify-staking/idl.json +0 -87
  52. package/dist/staking/commands.d.ts +0 -3
  53. package/dist/staking/commands.js +0 -13
  54. package/dist/staking/index.d.ts +0 -2
  55. package/dist/staking/index.js +0 -2
  56. package/dist/staking/program.d.ts +0 -18
  57. package/dist/staking/program.js +0 -40
  58. package/dist/types/errors.d.ts +0 -1
  59. package/dist/types/errors.js +0 -16
@@ -49,6 +49,57 @@ export async function withdrawIx(ctx, proof, root, nullifierHash, recipient, rel
49
49
  .instruction();
50
50
  return [withdrawComputeBudgetIx(), ix];
51
51
  }
52
+ function scoreRelayer(stake, feeBps) {
53
+ const fee = feeBps / 10000;
54
+ return stake * Math.max(0, 1 - 25 * fee * fee);
55
+ }
56
+ function pickRandomRelayer(list) {
57
+ if (list.length === 0)
58
+ return null;
59
+ if (list.length === 1)
60
+ return list[0];
61
+ const weights = list.map((r) => scoreRelayer(Number(r.stakeAmount ?? 0n), r.feeBps));
62
+ const total = weights.reduce((sum, weight) => sum + weight, 0);
63
+ if (total === 0)
64
+ return list[Math.floor(Math.random() * list.length)];
65
+ let rnd = Math.random() * total;
66
+ for (let i = 0; i < list.length; i++) {
67
+ rnd -= weights[i];
68
+ if (rnd <= 0)
69
+ return list[i];
70
+ }
71
+ return list[list.length - 1];
72
+ }
73
+ function findRelayerByName(relayers, name) {
74
+ return relayers.find((r) => r.name === name) ?? null;
75
+ }
76
+ async function isRelayerHealthy(url) {
77
+ try {
78
+ const res = await fetch(url.replace(/\/$/, "") + "/health", {
79
+ method: "GET",
80
+ signal: AbortSignal.timeout(5000),
81
+ });
82
+ return res.ok;
83
+ }
84
+ catch {
85
+ return false;
86
+ }
87
+ }
88
+ async function pickAutoRelayer(relayers) {
89
+ const active = relayers.filter((r) => r.isActive);
90
+ const healthChecks = await Promise.all(active.map(async (relayer) => ({
91
+ relayer,
92
+ isHealthy: await isRelayerHealthy(relayer.url),
93
+ })));
94
+ const healthy = healthChecks
95
+ .filter((item) => item.isHealthy)
96
+ .map((item) => item.relayer);
97
+ const selected = pickRandomRelayer(healthy);
98
+ if (!selected) {
99
+ throw new Error("Failed to auto-select relayer: no healthy relayer found");
100
+ }
101
+ return selected;
102
+ }
52
103
  export async function validateNote(ctx, note_str) {
53
104
  const note = await Note.deserialize(note_str);
54
105
  const voidifyProgram = new VoidifyProgram(ctx.connection, ctx.programId);
@@ -69,10 +120,10 @@ export async function validateNote(ctx, note_str) {
69
120
  }
70
121
  return { note };
71
122
  }
72
- export async function prepareWithdraw(ctx, note_str, recipient, relayer) {
123
+ export async function prepareWithdraw(ctx, note_str, recipient, relayerName, options, sendRpc = false) {
73
124
  const note = await Note.deserialize(note_str);
74
- const recipientPubkey = new PublicKey(recipient);
75
- const relayerPubkey = new PublicKey(relayer);
125
+ const recipientPubkey = recipient ? new PublicKey(recipient) : ctx.publicKey;
126
+ const recipientAddress = recipientPubkey.toBase58();
76
127
  const voidifyProgram = new VoidifyProgram(ctx.connection, ctx.programId);
77
128
  const nullifierBytes = bigIntToBytes(BigInt(note.nullifierHash));
78
129
  const nullifierPda = voidifyProgram.nullifier(nullifierBytes);
@@ -83,7 +134,7 @@ export async function prepareWithdraw(ctx, note_str, recipient, relayer) {
83
134
  const client = new SubstreamCliClient(ctx);
84
135
  await client.init();
85
136
  const depositModule = client.module("deposit");
86
- await depositModule.sync(note.amountRaw);
137
+ await depositModule.sync(note.amountRaw, options?.depositSync);
87
138
  const deposits = await depositModule.list(note.amountRaw);
88
139
  const commitments = [...deposits]
89
140
  .sort((a, b) => a.index - b.index)
@@ -95,11 +146,17 @@ export async function prepareWithdraw(ctx, note_str, recipient, relayer) {
95
146
  throw new Error("Invalid note: deposit not found in the pool. Verify the note is correct and the deposit transaction has been confirmed.");
96
147
  }
97
148
  const relayerModule = client.module("relayer");
98
- await relayerModule.sync();
99
- const relayerInfo = await relayerModule.get(relayer);
149
+ await relayerModule.sync(options?.relayerSync);
150
+ const relayers = await relayerModule.list();
151
+ const relayerInfo = relayerName
152
+ ? findRelayerByName(relayers, relayerName)
153
+ : await pickAutoRelayer(relayers);
100
154
  if (!relayerInfo) {
101
- throw new Error("Failed to get relayer info");
155
+ throw new Error(relayerName
156
+ ? `Failed to get relayer info for name: ${relayerName}`
157
+ : "Failed to get relayer info");
102
158
  }
159
+ const relayerPubkey = new PublicKey(relayerInfo.relayerPubkey);
103
160
  const treasuryConfig = await voidifyProgram.program.account.treasuryConfig.fetch(voidifyProgram.treasuryConfig());
104
161
  const fee = (note.amountRaw * BigInt(relayerInfo.feeBps)) / BigInt(10000);
105
162
  const treasury = (note.amountRaw * BigInt(treasuryConfig.treasuryBps)) / BigInt(10000);
@@ -108,10 +165,11 @@ export async function prepareWithdraw(ctx, note_str, recipient, relayer) {
108
165
  proof: await proofToBytes(proof),
109
166
  root: Array.from(bigIntToBytes(BigInt(root))),
110
167
  nullifierHash: Array.from(bigIntToBytes(BigInt(note.nullifierHash))),
111
- recipient,
168
+ recipient: recipientAddress,
112
169
  amount: note.amountRaw.toString(),
113
170
  fee: fee.toString(),
114
171
  treasury: treasury.toString(),
172
+ ...(sendRpc ? { rpcUrl: ctx.rpcUrl } : {}),
115
173
  });
116
174
  return { withdrawData, relayerUrl: relayerInfo.url };
117
175
  }
@@ -147,8 +205,8 @@ export async function submitWithdrawToRelayer(artifact) {
147
205
  }
148
206
  return result.signature;
149
207
  }
150
- export async function withdraw(ctx, note_str, recipient, relayer, _dryRun) {
151
- const artifact = await prepareWithdraw(ctx, note_str, recipient, relayer);
208
+ export async function withdraw(ctx, note_str, recipient, relayerName, options, sendRpc = false) {
209
+ const artifact = await prepareWithdraw(ctx, note_str, recipient, relayerName, options, sendRpc);
152
210
  return submitWithdrawToRelayer(artifact);
153
211
  }
154
212
  export async function directWithdraw(ctx, note_str, recipient) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voidifydao/sdk",
3
- "version": "1.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Voidify client SDK and CLI for interacting with the Anchor voidify program suite (browser + Node).",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -44,6 +44,7 @@
44
44
  "express": "^5.2.1",
45
45
  "ffjavascript": "^0.3.1",
46
46
  "fixed-merkle-tree": "^0.7.3",
47
+ "p-retry": "^8.0.0",
47
48
  "pino": "^10.3.1",
48
49
  "pino-pretty": "^13.1.3",
49
50
  "snarkjs": "^0.7.6",
@@ -70,10 +71,10 @@
70
71
  "voidify",
71
72
  "cli"
72
73
  ],
74
+ "author": "",
75
+ "license": "MIT",
73
76
  "repository": {
74
77
  "type": "git",
75
78
  "url": "git+https://github.com/VoidifyCommunity/voidify-sdk.git"
76
- },
77
- "author": "",
78
- "license": "MIT"
79
+ }
79
80
  }
@@ -1,93 +0,0 @@
1
- /**
2
- * Program IDL in camelCase format in order to be used in JS/TS.
3
- *
4
- * Note that this is only a type helper and is not the actual IDL. The original
5
- * IDL can be found at `target/idl/voidify_staking.json`.
6
- */
7
- export type VoidifyStaking = {
8
- "address": "";
9
- "metadata": {
10
- "name": "voidifyStaking";
11
- "version": "1.0.1";
12
- "spec": "0.1.0";
13
- };
14
- "instructions": [
15
- {
16
- "name": "notifyRewardAmount";
17
- "discriminator": [
18
- 228,
19
- 154,
20
- 113,
21
- 244,
22
- 155,
23
- 76,
24
- 153,
25
- 136
26
- ];
27
- "accounts": [
28
- {
29
- "name": "caller";
30
- "signer": true;
31
- },
32
- {
33
- "name": "stakingState";
34
- "writable": true;
35
- "pda": {
36
- "seeds": [
37
- {
38
- "kind": "const";
39
- "value": [
40
- 115,
41
- 116,
42
- 97,
43
- 107,
44
- 105,
45
- 110,
46
- 103,
47
- 95,
48
- 115,
49
- 116,
50
- 97,
51
- 116,
52
- 101
53
- ];
54
- }
55
- ];
56
- };
57
- },
58
- {
59
- "name": "rewardVault";
60
- "pda": {
61
- "seeds": [
62
- {
63
- "kind": "const";
64
- "value": [
65
- 114,
66
- 101,
67
- 119,
68
- 97,
69
- 114,
70
- 100,
71
- 95,
72
- 118,
73
- 97,
74
- 117,
75
- 108,
76
- 116
77
- ];
78
- }
79
- ];
80
- };
81
- },
82
- {
83
- "name": "tokenProgram";
84
- "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
85
- }
86
- ];
87
- "args": [];
88
- }
89
- ];
90
- "accounts": [];
91
- "events": [];
92
- "types": [];
93
- };
@@ -1 +0,0 @@
1
- export {};
@@ -1,87 +0,0 @@
1
- {
2
- "address": "3K12np4CKy8WbQim78trY6Pdii8wHQiWpQ4dMdsCKXYo",
3
- "metadata": {
4
- "name": "voidify_staking",
5
- "version": "1.0.1",
6
- "spec": "0.1.0"
7
- },
8
- "instructions": [
9
- {
10
- "name": "",
11
- "discriminator": [
12
- 228,
13
- 154,
14
- 113,
15
- 244,
16
- 155,
17
- 76,
18
- 153,
19
- 136
20
- ],
21
- "accounts": [
22
- {
23
- "name": "caller",
24
- "signer": true
25
- },
26
- {
27
- "name": "staking_state",
28
- "writable": true,
29
- "pda": {
30
- "seeds": [
31
- {
32
- "kind": "const",
33
- "value": [
34
- 115,
35
- 116,
36
- 97,
37
- 107,
38
- 105,
39
- 110,
40
- 103,
41
- 95,
42
- 115,
43
- 116,
44
- 97,
45
- 116,
46
- 101
47
- ]
48
- }
49
- ]
50
- }
51
- },
52
- {
53
- "name": "reward_vault",
54
- "pda": {
55
- "seeds": [
56
- {
57
- "kind": "const",
58
- "value": [
59
- 114,
60
- 101,
61
- 119,
62
- 97,
63
- 114,
64
- 100,
65
- 95,
66
- 118,
67
- 97,
68
- 117,
69
- 108,
70
- 116
71
- ]
72
- }
73
- ]
74
- }
75
- },
76
- {
77
- "name": "token_program",
78
- "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
79
- }
80
- ],
81
- "args": []
82
- }
83
- ],
84
- "accounts": [],
85
- "events": [],
86
- "types": []
87
- }
@@ -1,3 +0,0 @@
1
- import { PublicKey, TransactionInstruction } from "@solana/web3.js";
2
- import { Context } from "../context.js";
3
- export declare function notifyRewardAmountIx(ctx: Context, caller?: PublicKey): Promise<TransactionInstruction[]>;
@@ -1,13 +0,0 @@
1
- import { VoidifyStakingProgram } from "./program.js";
2
- export async function notifyRewardAmountIx(ctx, caller) {
3
- const staking = new VoidifyStakingProgram(ctx.connection, ctx.stakingProgramId);
4
- const ix = await staking.program.methods
5
- .notifyRewardAmount()
6
- .accountsPartial({
7
- caller: caller ?? ctx.publicKey,
8
- stakingState: staking.stakingState(),
9
- rewardVault: staking.rewardVault(),
10
- })
11
- .instruction();
12
- return [ix];
13
- }
@@ -1,2 +0,0 @@
1
- export { VoidifyStakingProgram } from "./program.js";
2
- export * from "./commands.js";
@@ -1,2 +0,0 @@
1
- export { VoidifyStakingProgram } from "./program.js";
2
- export * from "./commands.js";
@@ -1,18 +0,0 @@
1
- import { Program } from "@coral-xyz/anchor";
2
- import { Connection, PublicKey } from "@solana/web3.js";
3
- import type { VoidifyStaking } from "../idl/voidify-staking/idl.js";
4
- export declare class VoidifyStakingProgram {
5
- private connection;
6
- private _program;
7
- private _programId;
8
- static readonly SEEDS: {
9
- readonly STAKING_STATE: Buffer<ArrayBufferLike>;
10
- readonly REWARD_VAULT: Buffer<ArrayBufferLike>;
11
- };
12
- constructor(connection: Connection, programId: PublicKey);
13
- get program(): Program<VoidifyStaking>;
14
- get programId(): PublicKey;
15
- get rpcConnection(): Connection;
16
- stakingState(): PublicKey;
17
- rewardVault(): PublicKey;
18
- }
@@ -1,40 +0,0 @@
1
- import { Program } from "@coral-xyz/anchor";
2
- import { PublicKey } from "@solana/web3.js";
3
- import idl from "../idl/voidify-staking/idl.json" with { type: "json" };
4
- import { getConstSeed } from "../utils/idl-seed.js";
5
- export class VoidifyStakingProgram {
6
- connection;
7
- _program = null;
8
- _programId;
9
- static SEEDS = {
10
- STAKING_STATE: getConstSeed(idl, "staking_state"),
11
- REWARD_VAULT: getConstSeed(idl, "reward_vault"),
12
- };
13
- constructor(connection, programId) {
14
- this.connection = connection;
15
- this._programId = programId;
16
- }
17
- get program() {
18
- if (!this._program) {
19
- idl["address"] = this._programId.toBase58();
20
- this._program = new Program(idl, {
21
- connection: this.connection,
22
- });
23
- }
24
- return this._program;
25
- }
26
- get programId() {
27
- return this._programId;
28
- }
29
- get rpcConnection() {
30
- return this.connection;
31
- }
32
- stakingState() {
33
- const [pda] = PublicKey.findProgramAddressSync([VoidifyStakingProgram.SEEDS.STAKING_STATE], this._programId);
34
- return pda;
35
- }
36
- rewardVault() {
37
- const [pda] = PublicKey.findProgramAddressSync([VoidifyStakingProgram.SEEDS.REWARD_VAULT], this._programId);
38
- return pda;
39
- }
40
- }
@@ -1 +0,0 @@
1
- export declare function mapOnChainError(errorMsg: string): string;
@@ -1,16 +0,0 @@
1
- export function mapOnChainError(errorMsg) {
2
- if (errorMsg.includes("UnknownRoot") || errorMsg.includes("6005")) {
3
- return "Merkle root not in history, please confirm deposit is confirmed";
4
- }
5
- if (errorMsg.includes("Unauthorized") || errorMsg.includes("6006")) {
6
- return "Unauthorized to perform this operation";
7
- }
8
- if (errorMsg.includes("InvalidProof") || errorMsg.includes("6000")) {
9
- return "Zero-knowledge proof is invalid, please check deposit note";
10
- }
11
- if (errorMsg.includes("AccountAlreadyInUse") ||
12
- errorMsg.includes("custom program error: 0x0")) {
13
- return "This deposit note has already been used";
14
- }
15
- return errorMsg;
16
- }