@voidifydao/sdk 1.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 (115) hide show
  1. package/dist/cli/config/command.d.ts +2 -0
  2. package/dist/cli/config/command.js +91 -0
  3. package/dist/cli/config/init.d.ts +3 -0
  4. package/dist/cli/config/init.js +88 -0
  5. package/dist/cli/config/keypair.d.ts +4 -0
  6. package/dist/cli/config/keypair.js +35 -0
  7. package/dist/cli/config/loader.d.ts +11 -0
  8. package/dist/cli/config/loader.js +65 -0
  9. package/dist/cli/config/types.d.ts +50 -0
  10. package/dist/cli/config/types.js +33 -0
  11. package/dist/cli/deposit.d.ts +2 -0
  12. package/dist/cli/deposit.js +58 -0
  13. package/dist/cli/helpers.d.ts +12 -0
  14. package/dist/cli/helpers.js +53 -0
  15. package/dist/cli/note.d.ts +2 -0
  16. package/dist/cli/note.js +50 -0
  17. package/dist/cli/relayer.d.ts +2 -0
  18. package/dist/cli/relayer.js +60 -0
  19. package/dist/cli/substream.d.ts +2 -0
  20. package/dist/cli/substream.js +35 -0
  21. package/dist/cli/withdraw.d.ts +2 -0
  22. package/dist/cli/withdraw.js +23 -0
  23. package/dist/cli.d.ts +2 -0
  24. package/dist/cli.js +30 -0
  25. package/dist/context.d.ts +45 -0
  26. package/dist/context.js +77 -0
  27. package/dist/idl/voidify/idl.d.ts +1313 -0
  28. package/dist/idl/voidify/idl.js +1 -0
  29. package/dist/idl/voidify/idl.json +1307 -0
  30. package/dist/idl/voidify-staking/idl.d.ts +93 -0
  31. package/dist/idl/voidify-staking/idl.js +1 -0
  32. package/dist/idl/voidify-staking/idl.json +87 -0
  33. package/dist/index.d.ts +17 -0
  34. package/dist/index.js +10 -0
  35. package/dist/relayer/server/index.d.ts +6 -0
  36. package/dist/relayer/server/index.js +32 -0
  37. package/dist/relayer/server/server.d.ts +24 -0
  38. package/dist/relayer/server/server.js +158 -0
  39. package/dist/relayer/server/switchboard.d.ts +2 -0
  40. package/dist/relayer/server/switchboard.js +42 -0
  41. package/dist/relayer/types.d.ts +21 -0
  42. package/dist/relayer/types.js +1 -0
  43. package/dist/staking/commands.d.ts +3 -0
  44. package/dist/staking/commands.js +13 -0
  45. package/dist/staking/index.d.ts +2 -0
  46. package/dist/staking/index.js +2 -0
  47. package/dist/staking/program.d.ts +18 -0
  48. package/dist/staking/program.js +40 -0
  49. package/dist/substream/chain/events.d.ts +4 -0
  50. package/dist/substream/chain/events.js +50 -0
  51. package/dist/substream/chain/index.d.ts +24 -0
  52. package/dist/substream/chain/index.js +79 -0
  53. package/dist/substream/chain/registry.d.ts +44 -0
  54. package/dist/substream/chain/registry.js +28 -0
  55. package/dist/substream/chain/utils.d.ts +9 -0
  56. package/dist/substream/chain/utils.js +41 -0
  57. package/dist/substream/client.d.ts +27 -0
  58. package/dist/substream/client.js +28 -0
  59. package/dist/substream/database/indexeddb.d.ts +2 -0
  60. package/dist/substream/database/indexeddb.js +242 -0
  61. package/dist/substream/database/sqlite.d.ts +26 -0
  62. package/dist/substream/database/sqlite.js +275 -0
  63. package/dist/substream/modules/deposit.d.ts +14 -0
  64. package/dist/substream/modules/deposit.js +123 -0
  65. package/dist/substream/modules/index.d.ts +11 -0
  66. package/dist/substream/modules/index.js +7 -0
  67. package/dist/substream/modules/relayer.d.ts +10 -0
  68. package/dist/substream/modules/relayer.js +290 -0
  69. package/dist/substream/runtime.d.ts +38 -0
  70. package/dist/substream/runtime.js +163 -0
  71. package/dist/substream/server/event-listener.d.ts +18 -0
  72. package/dist/substream/server/event-listener.js +68 -0
  73. package/dist/substream/server/index.d.ts +3 -0
  74. package/dist/substream/server/index.js +30 -0
  75. package/dist/substream/server/server.d.ts +43 -0
  76. package/dist/substream/server/server.js +216 -0
  77. package/dist/substream/types.d.ts +94 -0
  78. package/dist/substream/types.js +1 -0
  79. package/dist/types/errors.d.ts +1 -0
  80. package/dist/types/errors.js +16 -0
  81. package/dist/types/events.d.ts +13 -0
  82. package/dist/types/events.js +1 -0
  83. package/dist/types/index.d.ts +3 -0
  84. package/dist/types/index.js +1 -0
  85. package/dist/utils/amount.d.ts +4 -0
  86. package/dist/utils/amount.js +41 -0
  87. package/dist/utils/anchor-events.d.ts +13 -0
  88. package/dist/utils/anchor-events.js +28 -0
  89. package/dist/utils/bytes.d.ts +10 -0
  90. package/dist/utils/bytes.js +29 -0
  91. package/dist/utils/idl-seed.d.ts +17 -0
  92. package/dist/utils/idl-seed.js +15 -0
  93. package/dist/utils/index.d.ts +2 -0
  94. package/dist/utils/index.js +2 -0
  95. package/dist/utils/logger.d.ts +3 -0
  96. package/dist/utils/logger.js +19 -0
  97. package/dist/utils/note.d.ts +19 -0
  98. package/dist/utils/note.js +83 -0
  99. package/dist/utils/proof.d.ts +11 -0
  100. package/dist/utils/proof.js +91 -0
  101. package/dist/utils/tx.d.ts +11 -0
  102. package/dist/utils/tx.js +62 -0
  103. package/dist/voidify/deposit.d.ts +10 -0
  104. package/dist/voidify/deposit.js +40 -0
  105. package/dist/voidify/index.d.ts +4 -0
  106. package/dist/voidify/index.js +4 -0
  107. package/dist/voidify/program.d.ts +36 -0
  108. package/dist/voidify/program.js +87 -0
  109. package/dist/voidify/relayer/index.d.ts +1 -0
  110. package/dist/voidify/relayer/index.js +1 -0
  111. package/dist/voidify/relayer/list.d.ts +5 -0
  112. package/dist/voidify/relayer/list.js +16 -0
  113. package/dist/voidify/withdraw.d.ts +16 -0
  114. package/dist/voidify/withdraw.js +188 -0
  115. package/package.json +79 -0
@@ -0,0 +1,216 @@
1
+ import express from "express";
2
+ import cors from "cors";
3
+ import Database from "better-sqlite3";
4
+ import { SQLiteEventStore, SQLiteProjectionStore, } from "../../substream/database/sqlite.js";
5
+ import { EventListener } from "./event-listener.js";
6
+ import { chainEventToWire } from "../../substream/chain/events.js";
7
+ import { createSubstreamRuntime, } from "../../substream/runtime.js";
8
+ import { substreamLogger as logger } from "../../utils/logger.js";
9
+ export class HttpServer {
10
+ runtime;
11
+ app;
12
+ server = null;
13
+ host;
14
+ port;
15
+ constructor(runtime, config) {
16
+ this.runtime = runtime;
17
+ this.port = config.port;
18
+ this.host = config.host || "0.0.0.0";
19
+ this.app = express();
20
+ this.setupMiddleware();
21
+ this.setupRoutes();
22
+ }
23
+ setupMiddleware() {
24
+ this.app.use(cors());
25
+ this.app.use(express.json());
26
+ this.app.use((req, _res, next) => {
27
+ logger.info({ method: req.method, path: req.path }, "HTTP request");
28
+ next();
29
+ });
30
+ }
31
+ setupRoutes() {
32
+ this.app.get("/health", this.handleHealth.bind(this));
33
+ this.app.get("/api/events/:scopeType/:scopeKey", this.handleGetEvents.bind(this));
34
+ this.app.use((_req, res) => {
35
+ res.status(404).json({ error: "Not found" });
36
+ });
37
+ this.app.use((err, req, res, _next) => {
38
+ logger.error({ err, method: req.method, path: req.path }, "unhandled express error");
39
+ res.status(500).json({ error: "Internal server error" });
40
+ });
41
+ }
42
+ async handleHealth(_req, res) {
43
+ res.json({
44
+ status: "ok",
45
+ timestamp: Date.now(),
46
+ });
47
+ }
48
+ async handleGetEvents(req, res) {
49
+ try {
50
+ const scope = this.parseScope(req, res);
51
+ if (!scope)
52
+ return;
53
+ const afterIndex = this.parseAfterIndex(req, res);
54
+ if (afterIndex === false)
55
+ return;
56
+ await this.runtime.syncLocal(scope);
57
+ const events = await this.runtime.events.getAfter(scope, afterIndex === null ? undefined : afterIndex);
58
+ const cursor = await this.runtime.events.getCursor(scope);
59
+ res.json({
60
+ events: events.map(chainEventToWire),
61
+ total: events.length,
62
+ cursor: cursor
63
+ ? {
64
+ lastIndex: cursor.lastIndex === null ? null : cursor.lastIndex.toString(),
65
+ lastSignature: cursor.lastSignature,
66
+ lastSyncAt: cursor.lastSyncAt,
67
+ }
68
+ : null,
69
+ });
70
+ }
71
+ catch (error) {
72
+ logger.error({ err: error, params: req.params }, "fetch chain events failed");
73
+ res.status(500).json({ error: "Failed to fetch events" });
74
+ }
75
+ }
76
+ parseScope(req, res) {
77
+ const scopeType = String(req.params.scopeType);
78
+ const scopeKey = String(req.params.scopeKey);
79
+ const scope = this.runtime.registry.parseScope(scopeType, scopeKey);
80
+ if (!scope) {
81
+ res.status(400).json({ error: "Invalid scopeType parameter" });
82
+ return null;
83
+ }
84
+ return scope;
85
+ }
86
+ parseAfterIndex(req, res) {
87
+ const raw = req.query.after_index;
88
+ if (raw === undefined)
89
+ return null;
90
+ if (Array.isArray(raw)) {
91
+ res.status(400).json({ error: "Invalid after_index parameter" });
92
+ return false;
93
+ }
94
+ try {
95
+ return BigInt(String(raw));
96
+ }
97
+ catch {
98
+ res.status(400).json({ error: "Invalid after_index parameter" });
99
+ return false;
100
+ }
101
+ }
102
+ async start() {
103
+ await this.runtime.initialize();
104
+ return new Promise((resolve, reject) => {
105
+ try {
106
+ this.server = this.app.listen(this.port, this.host, () => {
107
+ logger.info({ address: this.getAddress() }, "HTTP server listening");
108
+ resolve();
109
+ });
110
+ this.server.on("error", (error) => {
111
+ reject(error);
112
+ });
113
+ }
114
+ catch (error) {
115
+ reject(error);
116
+ }
117
+ });
118
+ }
119
+ async stop() {
120
+ return new Promise((resolve, reject) => {
121
+ if (!this.server) {
122
+ resolve();
123
+ return;
124
+ }
125
+ this.server.close((error) => {
126
+ if (error) {
127
+ reject(error);
128
+ }
129
+ else {
130
+ logger.info("HTTP server stopped");
131
+ this.server = null;
132
+ resolve();
133
+ }
134
+ });
135
+ });
136
+ }
137
+ getAddress() {
138
+ return `http://${this.host}:${this.port}`;
139
+ }
140
+ }
141
+ export class SubstreamService {
142
+ ctx;
143
+ options;
144
+ database;
145
+ eventListener;
146
+ httpServer;
147
+ program;
148
+ runtime;
149
+ isRunning = false;
150
+ constructor(ctx, options) {
151
+ this.ctx = ctx;
152
+ this.options = options;
153
+ this.database = new Database(options.dbPath);
154
+ const stores = {
155
+ events: new SQLiteEventStore(this.database),
156
+ projections: new SQLiteProjectionStore(this.database),
157
+ };
158
+ this.runtime = createSubstreamRuntime(ctx, stores, { mode: "local" });
159
+ this.program = this.runtime.voidify.program;
160
+ this.eventListener = new EventListener(this.program);
161
+ this.httpServer = new HttpServer(this.runtime, {
162
+ port: options.port,
163
+ host: options.host,
164
+ });
165
+ }
166
+ async start() {
167
+ if (this.isRunning) {
168
+ throw new Error("Service is already running");
169
+ }
170
+ try {
171
+ logger.info({ dbPath: this.options.dbPath }, "initializing database");
172
+ await this.runtime.initialize();
173
+ logger.info("starting event listeners");
174
+ await this.startEventListeners();
175
+ await this.httpServer.start();
176
+ this.isRunning = true;
177
+ logger.info("service ready");
178
+ }
179
+ catch (error) {
180
+ await this.cleanup();
181
+ throw error;
182
+ }
183
+ }
184
+ async stop() {
185
+ if (!this.isRunning) {
186
+ return;
187
+ }
188
+ await this.cleanup();
189
+ this.isRunning = false;
190
+ logger.info("service stopped");
191
+ }
192
+ async startEventListeners() {
193
+ for (const liveEvent of this.runtime.registry.liveEvents()) {
194
+ this.eventListener.registerHandler(liveEvent.eventName, async (event, signature, slot) => {
195
+ const record = await liveEvent.toRecord({
196
+ ctx: this.ctx,
197
+ voidify: this.runtime.voidify,
198
+ event,
199
+ signature,
200
+ slot,
201
+ });
202
+ await this.runtime.applyLiveRecord(record, record);
203
+ });
204
+ }
205
+ }
206
+ async cleanup() {
207
+ try {
208
+ this.eventListener.removeAllListeners();
209
+ await this.httpServer.stop();
210
+ this.database.close();
211
+ }
212
+ catch (error) {
213
+ logger.error({ err: error }, "cleanup error");
214
+ }
215
+ }
216
+ }
@@ -0,0 +1,94 @@
1
+ export type EventScopeType = string;
2
+ export interface EventScope {
3
+ scopeType: EventScopeType;
4
+ scopeKey: string;
5
+ }
6
+ export type ChainEventPayload = Record<string, unknown>;
7
+ export interface ChainEventRecord extends EventScope {
8
+ eventName: string;
9
+ eventIndex: bigint;
10
+ signature: string;
11
+ slot: number | null;
12
+ blockTime: number | null;
13
+ address: string;
14
+ payload: ChainEventPayload;
15
+ createdAt: number;
16
+ }
17
+ export interface ChainEventWire extends Omit<ChainEventRecord, "eventIndex"> {
18
+ eventIndex: string;
19
+ }
20
+ export interface DepositRecord {
21
+ denomination: bigint;
22
+ depositor: string;
23
+ commitment: string;
24
+ index: number;
25
+ timestamp: number;
26
+ signature: string;
27
+ }
28
+ export interface RelayerRecord {
29
+ relayerPubkey: string;
30
+ name: string;
31
+ url: string;
32
+ feeBps: number;
33
+ stakeAmount: bigint;
34
+ isActive: boolean;
35
+ totalWithdrawals: number;
36
+ totalSolEarned: bigint;
37
+ totalTokenDeducted: bigint;
38
+ lastUpdated: number;
39
+ }
40
+ export interface EventCursor {
41
+ scopeType: EventScopeType;
42
+ scopeKey: string;
43
+ lastIndex: bigint | null;
44
+ lastSignature: string | null;
45
+ lastSyncAt: number;
46
+ }
47
+ export type ApplyOutcome = {
48
+ kind: "applied";
49
+ cursor: bigint;
50
+ } | {
51
+ kind: "duplicate";
52
+ cursor: bigint;
53
+ } | {
54
+ kind: "gap";
55
+ expected: bigint;
56
+ got: bigint;
57
+ };
58
+ export interface EventStore {
59
+ initialize(): Promise<void>;
60
+ apply(scope: EventScope, event: ChainEventRecord): Promise<ApplyOutcome>;
61
+ applyBatch(scope: EventScope, events: ChainEventRecord[]): Promise<ApplyOutcome>;
62
+ list(scope: EventScope, opts?: {
63
+ offset?: number;
64
+ limit?: number;
65
+ }): Promise<ChainEventRecord[]>;
66
+ getAfter(scope: EventScope, afterIndex?: bigint): Promise<ChainEventRecord[]>;
67
+ count(scope: EventScope): Promise<number>;
68
+ getCursor(scope: EventScope): Promise<EventCursor | null>;
69
+ }
70
+ export interface EventProjection {
71
+ id: string;
72
+ matches(event: ChainEventRecord): boolean;
73
+ apply(events: ChainEventRecord[]): Promise<void>;
74
+ }
75
+ export type ProjectionStateValue = Record<string, unknown>;
76
+ export interface ProjectionStateRecord {
77
+ projectionId: string;
78
+ key: string;
79
+ value: ProjectionStateValue;
80
+ updatedAt: number;
81
+ lastEventIndex: bigint | null;
82
+ }
83
+ export interface ProjectionStore {
84
+ initialize(): Promise<void>;
85
+ get(projectionId: string, key: string): Promise<ProjectionStateRecord | null>;
86
+ put(record: ProjectionStateRecord): Promise<void>;
87
+ list(projectionId: string): Promise<ProjectionStateRecord[]>;
88
+ clear(projectionId: string): Promise<void>;
89
+ }
90
+ export interface SubstreamStores {
91
+ events: EventStore;
92
+ projections: ProjectionStore;
93
+ }
94
+ export type SubstreamRepos = SubstreamStores;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export declare function mapOnChainError(errorMsg: string): string;
@@ -0,0 +1,16 @@
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
+ }
@@ -0,0 +1,13 @@
1
+ import type { IdlEvents } from "@coral-xyz/anchor";
2
+ import type { Voidify } from "../idl/voidify/idl.js";
3
+ export type VoidifyEventMap = IdlEvents<Voidify>;
4
+ export type VoidifyEventName = keyof VoidifyEventMap;
5
+ export type DepositEvent = VoidifyEventMap["depositEvent"];
6
+ export type WithdrawalEvent = VoidifyEventMap["withdrawalEvent"];
7
+ export type DirectWithdrawalEvent = VoidifyEventMap["directWithdrawalEvent"];
8
+ export type RelayerRegisteredEvent = VoidifyEventMap["relayerRegisteredEvent"];
9
+ export type RelayerUnregisteredEvent = VoidifyEventMap["relayerUnregisteredEvent"];
10
+ export type RelayerDeactivatedEvent = VoidifyEventMap["relayerDeactivatedEvent"];
11
+ export type RelayerSlashedEvent = VoidifyEventMap["relayerSlashedEvent"];
12
+ export type RelayerUpdatedEvent = VoidifyEventMap["relayerUpdatedEvent"];
13
+ export type RelayerActivatedEvent = VoidifyEventMap["relayerActivatedEvent"];
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { VersionedTransaction } from "@solana/web3.js";
2
+ export * from "./errors.js";
3
+ export type SignReturn<S> = S extends false ? VersionedTransaction : string;
@@ -0,0 +1 @@
1
+ export * from "./errors.js";
@@ -0,0 +1,4 @@
1
+ import BN from "bn.js";
2
+ export declare function parseUnits(input: string, decimals: number): bigint;
3
+ export declare function formatUnits(value: bigint, decimals: number): string;
4
+ export declare function toBN(value: bigint): BN;
@@ -0,0 +1,41 @@
1
+ import BN from "bn.js";
2
+ const VALID_NUMBER_RE = /^[0-9]+(\.[0-9]+)?$/;
3
+ export function parseUnits(input, decimals) {
4
+ if (typeof input !== "string") {
5
+ throw new Error(`parseUnits: expected string, got ${typeof input}`);
6
+ }
7
+ if (!Number.isInteger(decimals) || decimals < 0) {
8
+ throw new Error(`parseUnits: invalid decimals ${decimals}`);
9
+ }
10
+ if (!VALID_NUMBER_RE.test(input)) {
11
+ throw new Error(`parseUnits: invalid number format "${input}"`);
12
+ }
13
+ const [intPart, fracPart = ""] = input.split(".");
14
+ if (intPart.length > 1 && intPart.startsWith("0")) {
15
+ throw new Error(`parseUnits: leading zeros not allowed "${input}"`);
16
+ }
17
+ if (fracPart.length > decimals) {
18
+ throw new Error(`parseUnits: too many fractional digits in "${input}" for ${decimals} decimals`);
19
+ }
20
+ const padded = fracPart.padEnd(decimals, "0");
21
+ const combined = (intPart + padded).replace(/^0+/, "") || "0";
22
+ return BigInt(combined);
23
+ }
24
+ export function formatUnits(value, decimals) {
25
+ if (typeof value !== "bigint") {
26
+ throw new Error(`formatUnits: expected bigint, got ${typeof value}`);
27
+ }
28
+ if (!Number.isInteger(decimals) || decimals < 0) {
29
+ throw new Error(`formatUnits: invalid decimals ${decimals}`);
30
+ }
31
+ const negative = value < 0n;
32
+ const abs = negative ? -value : value;
33
+ const padded = abs.toString().padStart(decimals + 1, "0");
34
+ const intPart = padded.slice(0, padded.length - decimals);
35
+ const fracPart = padded.slice(padded.length - decimals).replace(/0+$/, "");
36
+ const result = fracPart.length > 0 ? `${intPart}.${fracPart}` : intPart;
37
+ return negative ? `-${result}` : result;
38
+ }
39
+ export function toBN(value) {
40
+ return new BN(value.toString());
41
+ }
@@ -0,0 +1,13 @@
1
+ import type BN from "bn.js";
2
+ export declare function toBigInt(v: BN | bigint | number | string): bigint;
3
+ export type DecodedEvent<M, K extends keyof M = keyof M> = K extends keyof M ? {
4
+ name: K;
5
+ data: M[K];
6
+ } : never;
7
+ export declare function parseEventsFromLogs<M>(logs: string[], coder: {
8
+ decode: (data: string) => {
9
+ name: string;
10
+ data: unknown;
11
+ } | null;
12
+ }): DecodedEvent<M>[];
13
+ export declare function findEventByName<M, K extends keyof M & string>(events: DecodedEvent<M>[], name: K): M[K] | null;
@@ -0,0 +1,28 @@
1
+ export function toBigInt(v) {
2
+ if (typeof v === "bigint")
3
+ return v;
4
+ if (typeof v === "number")
5
+ return BigInt(v);
6
+ if (typeof v === "string")
7
+ return BigInt(v);
8
+ return BigInt(v.toString());
9
+ }
10
+ export function parseEventsFromLogs(logs, coder) {
11
+ const events = [];
12
+ for (const log of logs) {
13
+ if (!log.includes("Program data:"))
14
+ continue;
15
+ const encoded = log.split("Program data: ")[1];
16
+ const decoded = coder.decode(encoded);
17
+ if (decoded)
18
+ events.push(decoded);
19
+ }
20
+ return events;
21
+ }
22
+ export function findEventByName(events, name) {
23
+ for (const e of events) {
24
+ if (e.name === name)
25
+ return e.data;
26
+ }
27
+ return null;
28
+ }
@@ -0,0 +1,10 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ export declare const bigIntToBytes: (bigInt: bigint, length?: number) => Buffer;
3
+ export declare function bytesToBigInt(bytes: Uint8Array | Buffer): bigint;
4
+ export declare function bytesToHex(bytes: Uint8Array | Buffer): string;
5
+ export declare function hexToBytes(hex: string): Buffer;
6
+ export declare function generateRandomFieldElement(): bigint;
7
+ export declare function publicKeyToLoHi(publicKey: PublicKey): {
8
+ lo: bigint;
9
+ hi: bigint;
10
+ };
@@ -0,0 +1,29 @@
1
+ export const bigIntToBytes = (bigInt, length = 32) => {
2
+ const hexString = bigInt.toString(16).padStart(length * 2, "0");
3
+ return Buffer.from(hexString, "hex");
4
+ };
5
+ export function bytesToBigInt(bytes) {
6
+ const hex = bytesToHex(bytes);
7
+ return BigInt("0x" + hex);
8
+ }
9
+ export function bytesToHex(bytes) {
10
+ return Buffer.from(bytes).toString("hex");
11
+ }
12
+ export function hexToBytes(hex) {
13
+ const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
14
+ return Buffer.from(cleanHex, "hex");
15
+ }
16
+ export function generateRandomFieldElement() {
17
+ const randomBytes = new Uint8Array(31);
18
+ crypto.getRandomValues(randomBytes);
19
+ const hex = Array.from(randomBytes)
20
+ .map((b) => b.toString(16).padStart(2, "0"))
21
+ .join("");
22
+ return BigInt("0x" + hex);
23
+ }
24
+ export function publicKeyToLoHi(publicKey) {
25
+ const bytes = publicKey.toBytes();
26
+ const hi = BigInt("0x" + Buffer.from(bytes.slice(0, 16)).toString("hex"));
27
+ const lo = BigInt("0x" + Buffer.from(bytes.slice(16, 32)).toString("hex"));
28
+ return { lo, hi };
29
+ }
@@ -0,0 +1,17 @@
1
+ type IdlSeed = {
2
+ kind: string;
3
+ value?: number[];
4
+ };
5
+ type IdlAccount = {
6
+ pda?: {
7
+ seeds?: IdlSeed[];
8
+ };
9
+ };
10
+ type IdlInstruction = {
11
+ accounts?: IdlAccount[];
12
+ };
13
+ type IdlLike = {
14
+ instructions?: IdlInstruction[];
15
+ };
16
+ export declare function getConstSeed(idl: IdlLike, name: string): Buffer;
17
+ export {};
@@ -0,0 +1,15 @@
1
+ export function getConstSeed(idl, name) {
2
+ const target = Buffer.from(name);
3
+ for (const ix of idl.instructions ?? []) {
4
+ for (const acct of ix.accounts ?? []) {
5
+ for (const seed of acct.pda?.seeds ?? []) {
6
+ if (seed.kind === "const" && seed.value) {
7
+ const buf = Buffer.from(seed.value);
8
+ if (buf.equals(target))
9
+ return buf;
10
+ }
11
+ }
12
+ }
13
+ }
14
+ throw new Error(`const PDA seed "${name}" not found in IDL`);
15
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./bytes.js";
2
+ export * from "./tx.js";
@@ -0,0 +1,2 @@
1
+ export * from "./bytes.js";
2
+ export * from "./tx.js";
@@ -0,0 +1,3 @@
1
+ import pino from "pino";
2
+ export declare const substreamLogger: pino.Logger<never, boolean>;
3
+ export declare const relayerLogger: pino.Logger<never, boolean>;
@@ -0,0 +1,19 @@
1
+ import pino from "pino";
2
+ const isProduction = process.env.NODE_ENV === "production";
3
+ const logger = pino({
4
+ level: process.env.LOG_LEVEL || "info",
5
+ ...(isProduction
6
+ ? {}
7
+ : {
8
+ transport: {
9
+ target: "pino-pretty",
10
+ options: {
11
+ colorize: true,
12
+ translateTime: "SYS:yyyy-mm-dd HH:MM:ss.l",
13
+ ignore: "pid,hostname",
14
+ },
15
+ },
16
+ }),
17
+ });
18
+ export const substreamLogger = logger.child({ module: "substream" });
19
+ export const relayerLogger = logger.child({ module: "relayer" });
@@ -0,0 +1,19 @@
1
+ export declare class NoteError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ export declare const TOKEN_DECIMALS: Record<string, number>;
5
+ export declare class Note {
6
+ readonly token: string;
7
+ readonly amount: string;
8
+ readonly amountRaw: bigint;
9
+ readonly nullifier: string;
10
+ readonly secret: string;
11
+ readonly commitment: string;
12
+ readonly nullifierHash: string;
13
+ private constructor();
14
+ static generate(uiAmount: string, token?: string): Promise<Note>;
15
+ static deserialize(noteString: string): Promise<Note>;
16
+ serialize(): string;
17
+ verify(commitment: string): Promise<boolean>;
18
+ private static computeHashes;
19
+ }
@@ -0,0 +1,83 @@
1
+ import { generateRandomFieldElement } from "../utils/bytes.js";
2
+ import { parseUnits, formatUnits } from "../utils/amount.js";
3
+ export class NoteError extends Error {
4
+ constructor(message) {
5
+ super(message);
6
+ this.name = "NoteError";
7
+ }
8
+ }
9
+ export const TOKEN_DECIMALS = {
10
+ SOL: 9,
11
+ };
12
+ function decimalsForToken(token) {
13
+ const d = TOKEN_DECIMALS[token];
14
+ if (d === undefined) {
15
+ throw new NoteError(`Unknown token "${token}" — add to TOKEN_DECIMALS`);
16
+ }
17
+ return d;
18
+ }
19
+ export class Note {
20
+ token;
21
+ amount;
22
+ amountRaw;
23
+ nullifier;
24
+ secret;
25
+ commitment;
26
+ nullifierHash;
27
+ constructor(token, amount, amountRaw, nullifier, secret, commitment, nullifierHash) {
28
+ this.token = token;
29
+ this.amount = amount;
30
+ this.amountRaw = amountRaw;
31
+ this.nullifier = nullifier;
32
+ this.secret = secret;
33
+ this.commitment = commitment;
34
+ this.nullifierHash = nullifierHash;
35
+ }
36
+ static async generate(uiAmount, token = "SOL") {
37
+ const decimals = decimalsForToken(token);
38
+ const amountRaw = parseUnits(uiAmount, decimals);
39
+ const normalized = formatUnits(amountRaw, decimals);
40
+ const nullifier = generateRandomFieldElement();
41
+ const secret = generateRandomFieldElement();
42
+ const { commitment, nullifierHash } = await Note.computeHashes(nullifier, secret, amountRaw);
43
+ return new Note(token, normalized, amountRaw, nullifier.toString(), secret.toString(), commitment, nullifierHash);
44
+ }
45
+ static async deserialize(noteString) {
46
+ const parts = noteString.split("-");
47
+ if (parts.length !== 5) {
48
+ throw new NoteError(`Invalid note format: expected 5 parts, got ${parts.length}`);
49
+ }
50
+ const [prefix, token, uiAmount, nullifierStr, secretStr] = parts;
51
+ if (prefix !== "voidify") {
52
+ throw new NoteError(`Invalid prefix: expected 'voidify', got '${prefix}'`);
53
+ }
54
+ const decimals = decimalsForToken(token);
55
+ const amountRaw = parseUnits(uiAmount, decimals);
56
+ const normalized = formatUnits(amountRaw, decimals);
57
+ const nullifier = BigInt(nullifierStr);
58
+ const secret = BigInt(secretStr);
59
+ const { commitment, nullifierHash } = await Note.computeHashes(nullifier, secret, amountRaw);
60
+ return new Note(token, normalized, amountRaw, nullifier.toString(), secret.toString(), commitment, nullifierHash);
61
+ }
62
+ serialize() {
63
+ return `voidify-${this.token}-${this.amount}-${this.nullifier}-${this.secret}`;
64
+ }
65
+ async verify(commitment) {
66
+ const { commitment: computedCommitment, nullifierHash } = await Note.computeHashes(BigInt(this.nullifier), BigInt(this.secret), this.amountRaw);
67
+ if (commitment !== undefined) {
68
+ return commitment === computedCommitment;
69
+ }
70
+ return (computedCommitment === this.commitment &&
71
+ nullifierHash === this.nullifierHash);
72
+ }
73
+ static async computeHashes(nullifier, secret, amount) {
74
+ const { buildPoseidon } = await import("circomlibjs");
75
+ const poseidon = await buildPoseidon();
76
+ const commitment = poseidon([nullifier, secret, amount]);
77
+ const nullifierHash = poseidon([nullifier]);
78
+ return {
79
+ commitment: poseidon.F.toString(commitment),
80
+ nullifierHash: poseidon.F.toString(nullifierHash),
81
+ };
82
+ }
83
+ }
@@ -0,0 +1,11 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ export declare function generateMerkleProof(commitment: string, commitments: string[]): Promise<{
3
+ pathElements: import("fixed-merkle-tree").Element[];
4
+ pathIndices: number[];
5
+ root: import("fixed-merkle-tree").Element;
6
+ }>;
7
+ export declare function generateProof(nullifier: string, secret: string, amount: string, commitment: string, nullifierHash: string, recipient: PublicKey, relayer: PublicKey, fee: BigInt, refund: BigInt, commitments: string[], wasmPath: string, zkeyPath: string): Promise<{
8
+ root: import("fixed-merkle-tree").Element;
9
+ proof: import("snarkjs").Groth16Proof;
10
+ }>;
11
+ export declare const proofToBytes: (proof: any) => Promise<number[]>;