@voidifydao/sdk 1.0.0 → 2.0.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.
Files changed (59) hide show
  1. package/README.md +105 -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
package/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # Voidify SDK
2
+
3
+ [English](README.md) | [中文](docs/README.zh-CN.md) | [Русский](docs/README.ru.md) | [日本語](docs/README.ja.md)
4
+
5
+ `@voidifydao/sdk` is the Voidify SDK and CLI for deposits, private notes, relayer withdrawals, relayer services, and indexed protocol activity on Solana.
6
+
7
+ ## Install
8
+
9
+ As a library:
10
+
11
+ ```sh
12
+ npm install @voidifydao/sdk
13
+ ```
14
+
15
+ As a command-line tool:
16
+
17
+ ```sh
18
+ npm install -g @voidifydao/sdk
19
+ voidify --help
20
+ ```
21
+
22
+ ## CLI Setup
23
+
24
+ Generate a JSON configuration file, then fill in that same file:
25
+
26
+ ```sh
27
+ voidify config init --type default --path ./voidify.json
28
+ voidify -c ./voidify.json config set programId YOUR_VOIDIFY_PROGRAM_ID
29
+ voidify -c ./voidify.json config set keypair.path /absolute/path/to/solana-keypair.json
30
+ ```
31
+
32
+ For withdrawal proof generation, download the proof artifacts from the
33
+ [Voidify ceremony record v1.0.0 release](https://github.com/VoidifyCommunity/voidify-ceremony-record/releases/tag/v1.0.0).
34
+ Extract `withdraw.wasm` and `withdraw.zkey`, then place them at the
35
+ `proof.wasmPath` and `proof.zkeyPath` paths in the generated config.
36
+
37
+ All following commands use the generated `./voidify.json` file through `-c`.
38
+
39
+ ## CLI Commands
40
+
41
+ Generate a private note:
42
+
43
+ ```sh
44
+ voidify -c ./voidify.json note gen 1
45
+ ```
46
+
47
+ Deposit `1 SOL`. If `--commitment` is omitted, a new note is generated and printed:
48
+
49
+ ```sh
50
+ voidify -c ./voidify.json deposit 1
51
+ ```
52
+
53
+ List deposits in a pool:
54
+
55
+ ```sh
56
+ voidify -c ./voidify.json deposit list 1
57
+ ```
58
+
59
+ Withdraw through an automatically selected healthy relayer:
60
+
61
+ ```sh
62
+ voidify -c ./voidify.json withdraw "YOUR_PRIVATE_NOTE" --recipient RECIPIENT_SOLANA_ADDRESS
63
+ ```
64
+
65
+ Choose a relayer by name:
66
+
67
+ ```sh
68
+ voidify -c ./voidify.json withdraw "YOUR_PRIVATE_NOTE" --recipient RECIPIENT_SOLANA_ADDRESS --relayer RELAYER_NAME
69
+ ```
70
+
71
+ > Keep your private note secure. Anyone with the note can withdraw the deposit, and a lost note cannot be recovered.
72
+
73
+ ## Run a Relayer
74
+
75
+ Generate a relayer JSON configuration, fill in that file, then use it for relayer commands:
76
+
77
+ ```sh
78
+ voidify config init --type relayer --path ./relayer.json
79
+ voidify -c ./relayer.json relayer start
80
+ ```
81
+
82
+ ## SDK Usage
83
+
84
+ ```ts
85
+ import { Context, Note, voidify } from "@voidifydao/sdk";
86
+ import { Keypair, PublicKey } from "@solana/web3.js";
87
+
88
+ const wallet = Keypair.generate();
89
+ const ctx = new Context({
90
+ rpcUrl: "https://api.mainnet-beta.solana.com",
91
+ programId: new PublicKey("YOUR_VOIDIFY_PROGRAM_ID"),
92
+ wallet,
93
+ });
94
+
95
+ const note = await Note.generate("1");
96
+ const signature = await voidify.deposit(
97
+ ctx,
98
+ note.commitment,
99
+ 1_000_000_000n,
100
+ );
101
+
102
+ console.log(note.serialize(), signature);
103
+ ```
104
+
105
+ The package also exports withdrawal helpers, relayer types, `VoidifyProgram`, substream clients and stores, and amount/note utilities.
@@ -13,7 +13,6 @@ export function buildTemplate(type) {
13
13
  return {
14
14
  rpcUrl: defaults.rpcUrl,
15
15
  programId: REQUIRED("voidify program ID"),
16
- stakingProgramId: null,
17
16
  keypair: fileKeypairPlaceholder("user keypair"),
18
17
  substream: { ...defaults.substream },
19
18
  proof: { ...defaults.proof },
@@ -22,7 +21,6 @@ export function buildTemplate(type) {
22
21
  return {
23
22
  rpcUrl: defaults.rpcUrl,
24
23
  programId: REQUIRED("voidify program ID"),
25
- stakingProgramId: null,
26
24
  keypair: fileKeypairPlaceholder("relayer keypair"),
27
25
  relayerServer: { ...defaults.relayerServer },
28
26
  };
@@ -37,7 +35,6 @@ export function buildTemplate(type) {
37
35
  return {
38
36
  rpcUrl: defaults.rpcUrl,
39
37
  programId: REQUIRED("voidify program ID"),
40
- stakingProgramId: null,
41
38
  keypair: fileKeypairPlaceholder("keypair for this config"),
42
39
  substream: { ...defaults.substream },
43
40
  proof: { ...defaults.proof },
@@ -53,7 +50,6 @@ export function postInitHint(type) {
53
50
  "Next steps:",
54
51
  " - programId <- voidify main program",
55
52
  " - keypair.path <- your keypair JSON file path",
56
- " - stakingProgramId <- optional staking program ID",
57
53
  " (substream / proof already use defaults; no changes needed)",
58
54
  "",
59
55
  "Applicable commands: deposit / withdraw / note.",
@@ -63,8 +59,7 @@ export function postInitHint(type) {
63
59
  "Next steps:",
64
60
  " - programId <- voidify main program",
65
61
  " - keypair.path <- keypair for the relayer service itself",
66
- " - relayerServer.feedId <- Switchboard on-demand feed ID",
67
- " - stakingProgramId <- optional staking program ID",
62
+ " - relayerServer.feedId Switchboard on-demand feed ID",
68
63
  "",
69
64
  "Applicable commands: relayer start / relayer list.",
70
65
  ].join("\n");
@@ -80,7 +75,6 @@ export function postInitHint(type) {
80
75
  return [
81
76
  "Next steps: full template; fill in values as needed.",
82
77
  " required: programId, keypair.path, relayerServer.feedId",
83
- " optional: stakingProgramId",
84
78
  "",
85
79
  "Reminder: one config should use one keypair. Create separate config files and switch with -c when roles differ.",
86
80
  ].join("\n");
@@ -6,7 +6,6 @@ export function defaultUserConfigPath() {
6
6
  return path.join(defaultUserConfigDir(), "config.json");
7
7
  }
8
8
  const OPTIONAL_KEYS = [
9
- "stakingProgramId",
10
9
  "keypair",
11
10
  "keypair.type",
12
11
  "keypair.path",
@@ -20,7 +20,6 @@ export type SubstreamSource = {
20
20
  export interface VoidifyConfig {
21
21
  rpcUrl: string;
22
22
  programId: string;
23
- stakingProgramId?: string | null;
24
23
  keypair?: KeypairSource | null;
25
24
  substream?: SubstreamSource | null;
26
25
  proof?: {
@@ -3,27 +3,66 @@ import { deposit, listDeposits } from "../voidify/deposit.js";
3
3
  import { parseUnits } from "../utils/amount.js";
4
4
  import { Note } from "../utils/note.js";
5
5
  import { createServiceContext, SOL_DECIMALS, } from "../cli/helpers.js";
6
+ import { createCliProgressBar } from "../cli/progress.js";
6
7
  export function registerDepositCommands(program) {
7
8
  const depositCommand = new Command("deposit")
8
9
  .enablePositionalOptions()
10
+ .argument("<amount>", "Deposit amount in UI units, such as 1 for 1 SOL")
9
11
  .description("Deposit commands");
12
+ depositCommand.option("-c, --commitment <string>", "Commitment generated by the note gen command; if omitted, a new one is generated and printed");
10
13
  depositCommand
11
- .command("submit")
12
- .option("-c, --commitment <string>", "Commitment generated by the note gen command; if omitted, a new one is generated and printed")
13
- .requiredOption("-a, --amount <amount>", "Deposit amount in UI units, such as 1 for 1 SOL")
14
- .action(async (options) => {
14
+ .command("list <amount>")
15
+ .description("List records for a denomination pool, using UI units such as 1 for 1 SOL")
16
+ .option("--offset <number>", "Pagination offset")
17
+ .option("--limit <number>", "Page size")
18
+ .option("-o, --output <file>", "Write output to a file")
19
+ .action(async (amount, options) => {
20
+ try {
21
+ const ctx = await createServiceContext(program.opts());
22
+ const progress = createCliProgressBar("Sync deposits");
23
+ const deposits = await (async () => {
24
+ try {
25
+ return await listDeposits(ctx, parseUnits(amount, SOL_DECIMALS), {
26
+ offset: options.offset !== undefined
27
+ ? parseInt(options.offset)
28
+ : undefined,
29
+ limit: options.limit !== undefined
30
+ ? parseInt(options.limit)
31
+ : undefined,
32
+ output: options.output,
33
+ sync: { reporter: progress },
34
+ });
35
+ }
36
+ finally {
37
+ progress.finish();
38
+ }
39
+ })();
40
+ if (!options.output) {
41
+ console.log("Deposit records:", deposits);
42
+ }
43
+ }
44
+ catch (error) {
45
+ console.error("List deposits failed:", error);
46
+ process.exit(1);
47
+ }
48
+ });
49
+ depositCommand.action(async (amount, options) => {
50
+ if (!amount) {
51
+ depositCommand.help({ error: true });
52
+ return;
53
+ }
15
54
  try {
16
55
  const ctx = await createServiceContext(program.opts());
17
56
  let commitment = options.commitment;
18
57
  if (!commitment) {
19
- const note = await Note.generate(options.amount);
58
+ const note = await Note.generate(amount);
20
59
  commitment = note.commitment;
21
60
  console.log("Note generated:", {
22
61
  note: note.serialize(),
23
62
  commitment: note.commitment,
24
63
  });
25
64
  }
26
- const txSignature = await deposit(ctx, commitment, parseUnits(options.amount, SOL_DECIMALS));
65
+ const txSignature = await deposit(ctx, commitment, parseUnits(amount, SOL_DECIMALS));
27
66
  console.log("Transaction successful:", { txSignature });
28
67
  }
29
68
  catch (error) {
@@ -31,28 +70,5 @@ export function registerDepositCommands(program) {
31
70
  process.exit(1);
32
71
  }
33
72
  });
34
- depositCommand
35
- .command("list <amount>")
36
- .description("List records for a denomination pool, using UI units such as 1 for 1 SOL")
37
- .option("--offset <number>", "Pagination offset")
38
- .option("--limit <number>", "Page size")
39
- .option("-o, --output <file>", "Write output to a file")
40
- .action(async (amount, options) => {
41
- try {
42
- const ctx = await createServiceContext(program.opts());
43
- const deposits = await listDeposits(ctx, parseUnits(amount, SOL_DECIMALS), {
44
- offset: options.offset !== undefined
45
- ? parseInt(options.offset)
46
- : undefined,
47
- limit: options.limit !== undefined ? parseInt(options.limit) : undefined,
48
- output: options.output,
49
- });
50
- console.log("Deposit records:", deposits);
51
- }
52
- catch (error) {
53
- console.error("List deposits failed:", error);
54
- process.exit(1);
55
- }
56
- });
57
73
  program.addCommand(depositCommand);
58
74
  }
@@ -39,9 +39,6 @@ export async function contextFromConfig(cfg, cliKeypairPath) {
39
39
  return new Context({
40
40
  rpcUrl: cfg.rpcUrl,
41
41
  programId: cfg.programId ? new PublicKey(cfg.programId) : undefined,
42
- stakingProgramId: cfg.stakingProgramId
43
- ? new PublicKey(cfg.stakingProgramId)
44
- : undefined,
45
42
  wallet: keypair,
46
43
  substream,
47
44
  wasmPath: cfg.proof ? expandHome(cfg.proof.wasmPath) : undefined,
@@ -0,0 +1,5 @@
1
+ import type { SyncStatusReporter } from "../substream/types.js";
2
+ export interface CliProgressBar extends SyncStatusReporter {
3
+ finish(): void;
4
+ }
5
+ export declare function createCliProgressBar(label: string): CliProgressBar;
@@ -0,0 +1,37 @@
1
+ export function createCliProgressBar(label) {
2
+ if (!process.stderr.isTTY) {
3
+ return {
4
+ update() { },
5
+ finish() { },
6
+ };
7
+ }
8
+ let started = false;
9
+ let lastLength = 0;
10
+ function render(status) {
11
+ const progress = status.progress;
12
+ if (!progress)
13
+ return;
14
+ const width = 28;
15
+ const ratio = progress.total > 0 ? progress.current / progress.total : 1;
16
+ const complete = Math.min(width, Math.floor(ratio * width));
17
+ const bar = `${"#".repeat(complete)}${"-".repeat(width - complete)}`;
18
+ const percent = Math.floor(ratio * 100)
19
+ .toString()
20
+ .padStart(3, " ");
21
+ const text = `${label} [${bar}] ${percent}% ${progress.current}/${progress.total}`;
22
+ const padding = " ".repeat(Math.max(0, lastLength - text.length));
23
+ process.stderr.write(`\r${text}${padding}`);
24
+ started = true;
25
+ lastLength = text.length;
26
+ }
27
+ return {
28
+ update: render,
29
+ finish() {
30
+ if (!started)
31
+ return;
32
+ process.stderr.write("\n");
33
+ started = false;
34
+ lastLength = 0;
35
+ },
36
+ };
37
+ }
@@ -2,21 +2,30 @@ import { Command } from "commander";
2
2
  import { startRelayer } from "../relayer/server/index.js";
3
3
  import { listRelayers } from "../voidify/relayer/list.js";
4
4
  import { createServiceContext, contextFromConfig, loadCliConfig, } from "../cli/helpers.js";
5
+ import { createCliProgressBar } from "../cli/progress.js";
5
6
  export function registerRelayerCommands(program) {
6
7
  const relayerCommand = new Command("relayer")
7
8
  .enablePositionalOptions()
8
9
  .description("Relayer commands");
9
10
  relayerCommand
10
- .command("list")
11
- .description("List relayer information; with --pubkey, returns only that relayer")
12
- .option("--pubkey <pubkey>", "Specific relayer public key")
11
+ .command("list [pubkey]")
12
+ .description("List relayer information for a specific relayer")
13
13
  .option("-o, --output <file>", "Write output to a file")
14
- .action(async (options) => {
14
+ .action(async (pubkey, options) => {
15
15
  try {
16
16
  const ctx = await createServiceContext(program.opts());
17
- const relayers = await listRelayers(ctx, options.pubkey, {
18
- output: options.output,
19
- });
17
+ const progress = createCliProgressBar("Sync relayers");
18
+ const relayers = await (async () => {
19
+ try {
20
+ return await listRelayers(ctx, pubkey, {
21
+ output: options.output,
22
+ sync: { reporter: progress },
23
+ });
24
+ }
25
+ finally {
26
+ progress.finish();
27
+ }
28
+ })();
20
29
  console.log("Relayer records:", relayers);
21
30
  }
22
31
  catch (error) {
@@ -1,17 +1,31 @@
1
1
  import { withdraw } from "../voidify/withdraw.js";
2
2
  import { createServiceContext } from "../cli/helpers.js";
3
+ import { createCliProgressBar } from "../cli/progress.js";
3
4
  export function registerWithdrawCommands(program) {
4
5
  program
5
6
  .command("withdraw")
6
7
  .description("Withdraw through a relayer")
7
- .requiredOption("--note <string>", "Note generated when depositing")
8
- .requiredOption("--recipient <pubkey>", "Recipient address")
9
- .requiredOption("--relayer <pubkey>", "Relayer address")
10
- .option("--dry", "Dry run: print withdraw information without generating a proof")
11
- .action(async (options) => {
8
+ .argument("<note>", "Note generated when depositing")
9
+ .option("--recipient <pubkey>", "Recipient address")
10
+ .option("--relayer <name>", "Relayer name")
11
+ .option("--send-rpc", "Send this client's RPC URL to the relayer for this withdraw request")
12
+ .action(async (note, options) => {
12
13
  try {
13
14
  const ctx = await createServiceContext(program.opts());
14
- const result = await withdraw(ctx, options.note, options.recipient, options.relayer, options.dry ?? false);
15
+ const depositProgress = createCliProgressBar("Sync deposits");
16
+ const relayerProgress = createCliProgressBar("Sync relayers");
17
+ const result = await (async () => {
18
+ try {
19
+ return await withdraw(ctx, note, options.recipient, options.relayer, {
20
+ depositSync: { reporter: depositProgress },
21
+ relayerSync: { reporter: relayerProgress },
22
+ }, options.sendRpc ?? false);
23
+ }
24
+ finally {
25
+ depositProgress.finish();
26
+ relayerProgress.finish();
27
+ }
28
+ })();
15
29
  console.log("Withdraw successful:", { txSignature: result });
16
30
  process.exit(0);
17
31
  }
package/dist/context.d.ts CHANGED
@@ -17,7 +17,6 @@ export type SubstreamConfig = {
17
17
  export interface ContextOptions {
18
18
  rpcUrl?: string;
19
19
  programId?: PublicKey;
20
- stakingProgramId?: PublicKey;
21
20
  wallet?: anchor.Wallet | Keypair | null;
22
21
  substream?: SubstreamConfig;
23
22
  wasmPath?: string;
@@ -27,17 +26,15 @@ export declare class Context {
27
26
  private readonly _rpcUrl;
28
27
  private readonly _connection;
29
28
  private readonly _programId;
30
- private readonly _stakingProgramId;
31
29
  private readonly _substream;
32
30
  private readonly _wasmPath;
33
31
  private readonly _zkeyPath;
34
32
  readonly wallet: anchor.Wallet | null;
35
33
  constructor(opts?: ContextOptions);
34
+ withRpcUrl(rpcUrl: string): Context;
36
35
  get rpcUrl(): string;
37
36
  get connection(): Connection;
38
37
  get programId(): PublicKey;
39
- get stakingProgramId(): PublicKey;
40
- hasStakingProgramId(): boolean;
41
38
  get substream(): SubstreamConfig;
42
39
  get wasmPath(): string;
43
40
  get zkeyPath(): string;
package/dist/context.js CHANGED
@@ -4,7 +4,6 @@ export class Context {
4
4
  _rpcUrl;
5
5
  _connection;
6
6
  _programId;
7
- _stakingProgramId;
8
7
  _substream;
9
8
  _wasmPath;
10
9
  _zkeyPath;
@@ -12,10 +11,12 @@ export class Context {
12
11
  constructor(opts = {}) {
13
12
  this._rpcUrl = opts.rpcUrl ?? null;
14
13
  this._connection = opts.rpcUrl
15
- ? new Connection(opts.rpcUrl, "confirmed")
14
+ ? new Connection(opts.rpcUrl, {
15
+ commitment: "confirmed",
16
+ disableRetryOnRateLimit: true,
17
+ })
16
18
  : null;
17
19
  this._programId = opts.programId ?? null;
18
- this._stakingProgramId = opts.stakingProgramId ?? null;
19
20
  this._substream = opts.substream ?? null;
20
21
  this._wasmPath = opts.wasmPath ?? null;
21
22
  this._zkeyPath = opts.zkeyPath ?? null;
@@ -23,6 +24,16 @@ export class Context {
23
24
  this.wallet =
24
25
  w === null ? null : w instanceof Keypair ? new anchor.Wallet(w) : w;
25
26
  }
27
+ withRpcUrl(rpcUrl) {
28
+ return new Context({
29
+ rpcUrl,
30
+ programId: this._programId ?? undefined,
31
+ wallet: this.wallet,
32
+ substream: this._substream ?? undefined,
33
+ wasmPath: this._wasmPath ?? undefined,
34
+ zkeyPath: this._zkeyPath ?? undefined,
35
+ });
36
+ }
26
37
  get rpcUrl() {
27
38
  if (!this._rpcUrl) {
28
39
  throw new Error("rpcUrl is required for this operation");
@@ -41,15 +52,6 @@ export class Context {
41
52
  }
42
53
  return this._programId;
43
54
  }
44
- get stakingProgramId() {
45
- if (!this._stakingProgramId) {
46
- throw new Error("stakingProgramId is required for this operation");
47
- }
48
- return this._stakingProgramId;
49
- }
50
- hasStakingProgramId() {
51
- return this._stakingProgramId !== null;
52
- }
53
55
  get substream() {
54
56
  if (!this._substream) {
55
57
  throw new Error("substream config is required for this operation");
@@ -8,7 +8,7 @@ export type Voidify = {
8
8
  "address": "";
9
9
  "metadata": {
10
10
  "name": "voidify";
11
- "version": "1.0.1";
11
+ "version": "2.0.0";
12
12
  "spec": "0.1.0";
13
13
  };
14
14
  "instructions": [
@@ -2,7 +2,7 @@
2
2
  "address": "",
3
3
  "metadata": {
4
4
  "name": "voidify",
5
- "version": "1.0.1",
5
+ "version": "2.0.0",
6
6
  "spec": "0.1.0"
7
7
  },
8
8
  "instructions": [
package/dist/index.d.ts CHANGED
@@ -1,15 +1,14 @@
1
1
  export { Context } from "./context.js";
2
2
  export type { SubstreamConfig, SubstreamMode } from "./context.js";
3
3
  export * as voidify from "./voidify/index.js";
4
- export * as staking from "./staking/index.js";
5
4
  export { VoidifyProgram } from "./voidify/program.js";
6
- export { VoidifyStakingProgram } from "./staking/program.js";
7
5
  export type { WithdrawArtifact } from "./voidify/withdraw.js";
8
6
  export { SubstreamCliClient } from "./substream/client.js";
9
7
  export type { SubstreamCliConfig, EventsApiResponse, CursorWire, } from "./substream/client.js";
10
8
  export { makeIndexedDBStores } from "./substream/database/indexeddb.js";
11
9
  export type { DepositModuleApi, RelayerModuleApi, } from "./substream/modules/index.js";
12
- export type { DepositRecord, RelayerRecord, EventCursor, ApplyOutcome, ChainEventRecord, ChainEventWire, EventScope, EventStore, EventProjection, ProjectionStateRecord, ProjectionStateValue, ProjectionStore, SubstreamRepos, SubstreamStores, } from "./substream/types.js";
10
+ export type { ChainSyncOptions } from "./substream/chain/index.js";
11
+ export type { DepositRecord, RelayerRecord, EventCursor, SyncProgress, SyncPhase, SyncStatus, SyncStatusReporter, ApplyOutcome, ChainEventRecord, ChainEventWire, EventScope, EventStore, EventProjection, ProjectionStateRecord, ProjectionStateValue, ProjectionStore, SubstreamRepos, SubstreamStores, } from "./substream/types.js";
13
12
  export { Note, TOKEN_DECIMALS } from "./utils/note.js";
14
13
  export { parseUnits, formatUnits, toBN } from "./utils/amount.js";
15
14
  export type { Voidify } from "./idl/voidify/idl.js";
package/dist/index.js CHANGED
@@ -1,8 +1,6 @@
1
1
  export { Context } from "./context.js";
2
2
  export * as voidify from "./voidify/index.js";
3
- export * as staking from "./staking/index.js";
4
3
  export { VoidifyProgram } from "./voidify/program.js";
5
- export { VoidifyStakingProgram } from "./staking/program.js";
6
4
  export { SubstreamCliClient } from "./substream/client.js";
7
5
  export { makeIndexedDBStores } from "./substream/database/indexeddb.js";
8
6
  export { Note, TOKEN_DECIMALS } from "./utils/note.js";
@@ -2,7 +2,6 @@ import express from "express";
2
2
  import cors from "cors";
3
3
  import { updateQuote } from "./switchboard.js";
4
4
  import { withdrawIx } from "../../voidify/withdraw.js";
5
- import { notifyRewardAmountIx } from "../../staking/commands.js";
6
5
  import { signAndSend } from "../../utils/tx.js";
7
6
  import { relayerLogger as logger } from "../../utils/logger.js";
8
7
  export class RelayerHttpServer {
@@ -58,14 +57,13 @@ export class RelayerHttpServer {
58
57
  });
59
58
  return;
60
59
  }
61
- await updateQuote(this.ctx, this.feedId);
62
- const switchboardQuote = await this.getSwitchboardQuote();
63
- const ixs = await withdrawIx(this.ctx, new Uint8Array(body.proof), new Uint8Array(body.root), new Uint8Array(body.nullifierHash), body.recipient, this.ctx.publicKey.toBase58(), BigInt(body.fee), BigInt(body.treasury), switchboardQuote, BigInt(body.amount));
64
- if (this.ctx.hasStakingProgramId()) {
65
- const notifyIxs = await notifyRewardAmountIx(this.ctx, this.ctx.publicKey);
66
- ixs.push(...notifyIxs);
67
- }
68
- const signature = await signAndSend(this.ctx, ixs);
60
+ const requestCtx = body.rpcUrl
61
+ ? this.ctx.withRpcUrl(body.rpcUrl)
62
+ : this.ctx;
63
+ await updateQuote(requestCtx, this.feedId);
64
+ const switchboardQuote = await this.getSwitchboardQuote(requestCtx);
65
+ const ixs = await withdrawIx(requestCtx, new Uint8Array(body.proof), new Uint8Array(body.root), new Uint8Array(body.nullifierHash), body.recipient, requestCtx.publicKey.toBase58(), BigInt(body.fee), BigInt(body.treasury), switchboardQuote, BigInt(body.amount));
66
+ const signature = await signAndSend(requestCtx, ixs);
69
67
  logger.info({ signature }, "withdraw submitted");
70
68
  res.json({
71
69
  success: true,
@@ -73,10 +71,11 @@ export class RelayerHttpServer {
73
71
  });
74
72
  }
75
73
  catch (error) {
76
- logger.error({ error }, "Failed to withdraw");
74
+ let error_msg = error instanceof Error ? error.message : String(error);
75
+ logger.error({ error_msg }, "Failed to withdraw");
77
76
  res.status(500).json({
78
77
  success: false,
79
- error: error instanceof Error ? error.message : String(error),
78
+ error: error_msg,
80
79
  });
81
80
  }
82
81
  }
@@ -97,6 +96,23 @@ export class RelayerHttpServer {
97
96
  if (!body.recipient || typeof body.recipient !== "string") {
98
97
  return "Invalid recipient: must be a string";
99
98
  }
99
+ if (body.rpcUrl !== undefined) {
100
+ if (typeof body.rpcUrl !== "string") {
101
+ return "Invalid rpcUrl: must be a string";
102
+ }
103
+ if (body.rpcUrl.length > 2048) {
104
+ return "Invalid rpcUrl: too long";
105
+ }
106
+ try {
107
+ const parsed = new URL(body.rpcUrl);
108
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
109
+ return "Invalid rpcUrl: must use http or https";
110
+ }
111
+ }
112
+ catch {
113
+ return "Invalid rpcUrl: not a valid URL";
114
+ }
115
+ }
100
116
  for (const field of ["fee", "treasury", "amount"]) {
101
117
  const value = body[field];
102
118
  if (value === undefined || value === null) {
@@ -113,9 +129,9 @@ export class RelayerHttpServer {
113
129
  }
114
130
  return null;
115
131
  }
116
- async getSwitchboardQuote() {
132
+ async getSwitchboardQuote(ctx) {
117
133
  const sb = await import("@switchboard-xyz/on-demand");
118
- const queue = await sb.Queue.loadDefault(await sb.AnchorUtils.loadProgramFromConnection(this.ctx.connection));
134
+ const queue = await sb.Queue.loadDefault(await sb.AnchorUtils.loadProgramFromConnection(ctx.connection));
119
135
  const [quotePDA] = sb.OracleQuote.getCanonicalPubkey(queue.pubkey, [
120
136
  this.feedId,
121
137
  ]);