@nosana/kit 0.1.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 (98) hide show
  1. package/.gitlab-ci.yml +21 -0
  2. package/README.md +159 -0
  3. package/dist/config/defaultConfigs.d.ts +2 -0
  4. package/dist/config/defaultConfigs.js +47 -0
  5. package/dist/config/index.d.ts +3 -0
  6. package/dist/config/index.js +19 -0
  7. package/dist/config/types.d.ts +43 -0
  8. package/dist/config/types.js +16 -0
  9. package/dist/config/utils.d.ts +3 -0
  10. package/dist/config/utils.js +35 -0
  11. package/dist/errors/NosanaError.d.ts +16 -0
  12. package/dist/errors/NosanaError.js +22 -0
  13. package/dist/generated_clients/jobs/accounts/index.d.ts +10 -0
  14. package/dist/generated_clients/jobs/accounts/index.js +26 -0
  15. package/dist/generated_clients/jobs/accounts/jobAccount.d.ts +47 -0
  16. package/dist/generated_clients/jobs/accounts/jobAccount.js +86 -0
  17. package/dist/generated_clients/jobs/accounts/marketAccount.d.ts +46 -0
  18. package/dist/generated_clients/jobs/accounts/marketAccount.js +82 -0
  19. package/dist/generated_clients/jobs/accounts/runAccount.d.ts +35 -0
  20. package/dist/generated_clients/jobs/accounts/runAccount.js +74 -0
  21. package/dist/generated_clients/jobs/errors/index.d.ts +8 -0
  22. package/dist/generated_clients/jobs/errors/index.js +24 -0
  23. package/dist/generated_clients/jobs/errors/nosanaJobs.d.ts +57 -0
  24. package/dist/generated_clients/jobs/errors/nosanaJobs.js +85 -0
  25. package/dist/generated_clients/jobs/index.d.ts +12 -0
  26. package/dist/generated_clients/jobs/index.js +28 -0
  27. package/dist/generated_clients/jobs/instructions/claim.d.ts +60 -0
  28. package/dist/generated_clients/jobs/instructions/claim.js +102 -0
  29. package/dist/generated_clients/jobs/instructions/clean.d.ts +42 -0
  30. package/dist/generated_clients/jobs/instructions/clean.js +79 -0
  31. package/dist/generated_clients/jobs/instructions/cleanAdmin.d.ts +42 -0
  32. package/dist/generated_clients/jobs/instructions/cleanAdmin.js +79 -0
  33. package/dist/generated_clients/jobs/instructions/close.d.ts +48 -0
  34. package/dist/generated_clients/jobs/instructions/close.js +90 -0
  35. package/dist/generated_clients/jobs/instructions/closeAdmin.d.ts +48 -0
  36. package/dist/generated_clients/jobs/instructions/closeAdmin.js +90 -0
  37. package/dist/generated_clients/jobs/instructions/complete.d.ts +43 -0
  38. package/dist/generated_clients/jobs/instructions/complete.js +82 -0
  39. package/dist/generated_clients/jobs/instructions/delist.d.ts +54 -0
  40. package/dist/generated_clients/jobs/instructions/delist.js +96 -0
  41. package/dist/generated_clients/jobs/instructions/end.d.ts +60 -0
  42. package/dist/generated_clients/jobs/instructions/end.js +102 -0
  43. package/dist/generated_clients/jobs/instructions/extend.d.ts +64 -0
  44. package/dist/generated_clients/jobs/instructions/extend.js +111 -0
  45. package/dist/generated_clients/jobs/instructions/finish.d.ts +67 -0
  46. package/dist/generated_clients/jobs/instructions/finish.js +111 -0
  47. package/dist/generated_clients/jobs/instructions/index.d.ts +25 -0
  48. package/dist/generated_clients/jobs/instructions/index.js +41 -0
  49. package/dist/generated_clients/jobs/instructions/list.d.ts +76 -0
  50. package/dist/generated_clients/jobs/instructions/list.js +126 -0
  51. package/dist/generated_clients/jobs/instructions/open.d.ts +73 -0
  52. package/dist/generated_clients/jobs/instructions/open.js +121 -0
  53. package/dist/generated_clients/jobs/instructions/quit.d.ts +45 -0
  54. package/dist/generated_clients/jobs/instructions/quit.js +82 -0
  55. package/dist/generated_clients/jobs/instructions/quitAdmin.d.ts +42 -0
  56. package/dist/generated_clients/jobs/instructions/quitAdmin.js +79 -0
  57. package/dist/generated_clients/jobs/instructions/recover.d.ts +54 -0
  58. package/dist/generated_clients/jobs/instructions/recover.js +96 -0
  59. package/dist/generated_clients/jobs/instructions/stop.d.ts +42 -0
  60. package/dist/generated_clients/jobs/instructions/stop.js +79 -0
  61. package/dist/generated_clients/jobs/instructions/update.d.ts +58 -0
  62. package/dist/generated_clients/jobs/instructions/update.js +93 -0
  63. package/dist/generated_clients/jobs/instructions/work.d.ts +57 -0
  64. package/dist/generated_clients/jobs/instructions/work.js +99 -0
  65. package/dist/generated_clients/jobs/programs/index.d.ts +8 -0
  66. package/dist/generated_clients/jobs/programs/index.js +24 -0
  67. package/dist/generated_clients/jobs/programs/nosanaJobs.d.ts +78 -0
  68. package/dist/generated_clients/jobs/programs/nosanaJobs.js +112 -0
  69. package/dist/generated_clients/jobs/shared/index.d.ts +49 -0
  70. package/dist/generated_clients/jobs/shared/index.js +94 -0
  71. package/dist/generated_clients/jobs/types/index.d.ts +10 -0
  72. package/dist/generated_clients/jobs/types/index.js +26 -0
  73. package/dist/generated_clients/jobs/types/jobState.d.ts +18 -0
  74. package/dist/generated_clients/jobs/types/jobState.js +30 -0
  75. package/dist/generated_clients/jobs/types/jobType.d.ts +21 -0
  76. package/dist/generated_clients/jobs/types/jobType.js +33 -0
  77. package/dist/generated_clients/jobs/types/queueType.d.ts +18 -0
  78. package/dist/generated_clients/jobs/types/queueType.js +30 -0
  79. package/dist/index.d.ts +19 -0
  80. package/dist/index.js +43 -0
  81. package/dist/ipfs/IPFS.d.ts +19 -0
  82. package/dist/ipfs/IPFS.js +47 -0
  83. package/dist/logger/Logger.d.ts +24 -0
  84. package/dist/logger/Logger.js +63 -0
  85. package/dist/programs/BaseProgram.d.ts +20 -0
  86. package/dist/programs/BaseProgram.js +39 -0
  87. package/dist/programs/JobsProgram.d.ts +118 -0
  88. package/dist/programs/JobsProgram.js +611 -0
  89. package/dist/solana/SolanaUtils.d.ts +15 -0
  90. package/dist/solana/SolanaUtils.js +48 -0
  91. package/dist/utils/index.d.ts +19 -0
  92. package/dist/utils/index.js +17 -0
  93. package/examples/node/monitor.ts +143 -0
  94. package/examples/node/package-lock.json +245 -0
  95. package/examples/node/package.json +16 -0
  96. package/examples/node/retrieve.ts +18 -0
  97. package/package.json +47 -0
  98. package/scripts/generate-clients.ts +9 -0
@@ -0,0 +1,611 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.JobsProgram = void 0;
40
+ const BaseProgram_js_1 = require("./BaseProgram.js");
41
+ const gill_1 = require("gill");
42
+ const index_js_1 = require("../index.js");
43
+ const programClient = __importStar(require("../generated_clients/jobs/index.js"));
44
+ const token_1 = require("@solana-program/token");
45
+ const bs58_1 = __importDefault(require("bs58"));
46
+ const IPFS_js_1 = require("../ipfs/IPFS.js");
47
+ const index_js_2 = require("../utils/index.js");
48
+ class JobsProgram extends BaseProgram_js_1.BaseProgram {
49
+ constructor(sdk) {
50
+ super(sdk);
51
+ this.client = programClient;
52
+ }
53
+ getProgramId() {
54
+ return this.sdk.config.programs.jobsAddress;
55
+ }
56
+ /**
57
+ * Fetch a job account by address
58
+ */
59
+ async get(addr, checkRun = true) {
60
+ try {
61
+ const jobAccount = await this.client.fetchJobAccount(this.sdk.solana.rpc, addr);
62
+ const job = this.transformJobAccount(jobAccount);
63
+ if (checkRun && job.state === 0) {
64
+ // If job is queued, check if there is a run account for the job
65
+ const runs = await this.runs({ job: job.address });
66
+ if (runs.length > 0) {
67
+ const run = runs[0];
68
+ job.state = 1;
69
+ job.timeStart = run.time;
70
+ job.node = run.node;
71
+ }
72
+ }
73
+ return job;
74
+ }
75
+ catch (err) {
76
+ this.sdk.logger.error(`Failed to fetch job ${err}`);
77
+ throw err;
78
+ }
79
+ }
80
+ /**
81
+ * Fetch a run account by address
82
+ */
83
+ async run(addr) {
84
+ try {
85
+ const runAccount = await this.client.fetchRunAccount(this.sdk.solana.rpc, addr);
86
+ const run = this.transformRunAccount(runAccount);
87
+ return run;
88
+ }
89
+ catch (err) {
90
+ this.sdk.logger.error(`Failed to fetch run ${err}`);
91
+ throw err;
92
+ }
93
+ }
94
+ /**
95
+ * Fetch a run account by address
96
+ */
97
+ async market(addr) {
98
+ try {
99
+ const marketAccount = await this.client.fetchMarketAccount(this.sdk.solana.rpc, addr);
100
+ const market = this.transformMarketAccount(marketAccount);
101
+ return market;
102
+ }
103
+ catch (err) {
104
+ this.sdk.logger.error(`Failed to fetch market ${err}`);
105
+ throw err;
106
+ }
107
+ }
108
+ /**
109
+ * Fetch multiple job accounts by address
110
+ */
111
+ async multiple(addresses, checkRuns = false) {
112
+ try {
113
+ const jobAccounts = await this.client.fetchAllJobAccount(this.sdk.solana.rpc, addresses);
114
+ ;
115
+ const jobs = jobAccounts.map(jobAccount => (this.transformJobAccount(jobAccount)));
116
+ if (checkRuns) {
117
+ const runs = await this.runs();
118
+ jobs.forEach(job => {
119
+ if (job.state === 0) {
120
+ const run = runs.find(run => run.job === job.address);
121
+ if (run) {
122
+ job.state = 1;
123
+ job.timeStart = run.time;
124
+ job.node = run.node;
125
+ }
126
+ }
127
+ });
128
+ }
129
+ return jobs;
130
+ }
131
+ catch (err) {
132
+ this.sdk.logger.error(`Failed to fetch job ${err}`);
133
+ throw err;
134
+ }
135
+ }
136
+ /**
137
+ * Fetch all job accounts
138
+ */
139
+ async all(filters, checkRuns = false) {
140
+ try {
141
+ const extraGPAFilters = [];
142
+ if (filters) {
143
+ if (typeof filters.state === 'number') {
144
+ extraGPAFilters.push({
145
+ memcmp: {
146
+ offset: BigInt(208),
147
+ bytes: bs58_1.default.encode(Buffer.from([filters.state])),
148
+ encoding: "base58",
149
+ },
150
+ });
151
+ }
152
+ if (filters.project) {
153
+ extraGPAFilters.push({
154
+ memcmp: {
155
+ offset: BigInt(176),
156
+ bytes: filters.project.toString(),
157
+ encoding: "base58",
158
+ },
159
+ });
160
+ }
161
+ if (filters.node) {
162
+ extraGPAFilters.push({
163
+ memcmp: {
164
+ offset: BigInt(104),
165
+ bytes: filters.node.toString(),
166
+ encoding: "base58",
167
+ },
168
+ });
169
+ }
170
+ if (filters.market) {
171
+ extraGPAFilters.push({
172
+ memcmp: {
173
+ offset: BigInt(72),
174
+ bytes: filters.market.toString(),
175
+ encoding: "base58",
176
+ },
177
+ });
178
+ }
179
+ }
180
+ const getProgramAccountsResponse = await this.sdk.solana.rpc
181
+ .getProgramAccounts(this.getProgramId(), {
182
+ encoding: "base64",
183
+ filters: [
184
+ {
185
+ memcmp: {
186
+ offset: BigInt(0),
187
+ bytes: bs58_1.default.encode(Buffer.from(programClient.JOB_ACCOUNT_DISCRIMINATOR)),
188
+ encoding: "base58",
189
+ },
190
+ },
191
+ ...extraGPAFilters,
192
+ ],
193
+ })
194
+ .send();
195
+ const jobs = getProgramAccountsResponse
196
+ .map((result) => {
197
+ try {
198
+ const jobAccount = programClient.decodeJobAccount((0, gill_1.parseBase64RpcAccount)(result.pubkey, result.account));
199
+ return this.transformJobAccount(jobAccount);
200
+ }
201
+ catch (err) {
202
+ this.sdk.logger.error(`Failed to decode job ${err}`);
203
+ return null;
204
+ }
205
+ })
206
+ .filter((account) => account !== null);
207
+ if (checkRuns) {
208
+ const runs = await this.runs();
209
+ jobs.forEach(job => {
210
+ if (job.state === 0) {
211
+ const run = runs.find(run => run.job === job.address);
212
+ if (run) {
213
+ job.state = 1;
214
+ job.timeStart = run.time;
215
+ job.node = run.node;
216
+ }
217
+ }
218
+ });
219
+ }
220
+ return jobs;
221
+ }
222
+ catch (err) {
223
+ this.sdk.logger.error(`Failed to fetch all jobs ${err}`);
224
+ throw err;
225
+ }
226
+ }
227
+ /**
228
+ * Fetch all run accounts
229
+ */
230
+ async runs(filters) {
231
+ try {
232
+ const extraGPAFilters = [];
233
+ if (filters) {
234
+ if (filters.node) {
235
+ extraGPAFilters.push({
236
+ memcmp: {
237
+ offset: BigInt(40),
238
+ bytes: filters.node.toString(),
239
+ encoding: "base58",
240
+ },
241
+ });
242
+ }
243
+ if (filters.job) {
244
+ extraGPAFilters.push({
245
+ memcmp: {
246
+ offset: BigInt(8),
247
+ bytes: filters.job.toString(),
248
+ encoding: "base58",
249
+ },
250
+ });
251
+ }
252
+ }
253
+ const getProgramAccountsResponse = await this.sdk.solana.rpc
254
+ .getProgramAccounts(this.getProgramId(), {
255
+ encoding: "base64",
256
+ filters: [
257
+ {
258
+ memcmp: {
259
+ offset: BigInt(0),
260
+ bytes: bs58_1.default.encode(Buffer.from(programClient.RUN_ACCOUNT_DISCRIMINATOR)),
261
+ encoding: "base58",
262
+ },
263
+ },
264
+ ],
265
+ })
266
+ .send();
267
+ const runAccounts = getProgramAccountsResponse
268
+ .map((result) => {
269
+ try {
270
+ const runAccount = programClient.decodeRunAccount((0, gill_1.parseBase64RpcAccount)(result.pubkey, result.account));
271
+ return this.transformRunAccount(runAccount);
272
+ }
273
+ catch (err) {
274
+ this.sdk.logger.error(`Failed to decode run ${err}`);
275
+ return null;
276
+ }
277
+ })
278
+ .filter((account) => account !== null);
279
+ return runAccounts;
280
+ }
281
+ catch (err) {
282
+ this.sdk.logger.error(`Failed to fetch all runs ${err}`);
283
+ throw err;
284
+ }
285
+ }
286
+ /**
287
+ * Fetch all market accounts
288
+ */
289
+ async markets() {
290
+ try {
291
+ const getProgramAccountsResponse = await this.sdk.solana.rpc
292
+ .getProgramAccounts(this.getProgramId(), {
293
+ encoding: "base64",
294
+ filters: [
295
+ {
296
+ memcmp: {
297
+ offset: BigInt(0),
298
+ bytes: bs58_1.default.encode(Buffer.from(programClient.MARKET_ACCOUNT_DISCRIMINATOR)),
299
+ encoding: "base58",
300
+ },
301
+ },
302
+ ],
303
+ })
304
+ .send();
305
+ const marketAccounts = getProgramAccountsResponse
306
+ .map((result) => {
307
+ try {
308
+ const marketAccount = programClient.decodeMarketAccount((0, gill_1.parseBase64RpcAccount)(result.pubkey, result.account));
309
+ return this.transformMarketAccount(marketAccount);
310
+ }
311
+ catch (err) {
312
+ this.sdk.logger.error(`Failed to decode market ${err}`);
313
+ return null;
314
+ }
315
+ })
316
+ .filter((account) => account !== null);
317
+ return marketAccounts;
318
+ }
319
+ catch (err) {
320
+ this.sdk.logger.error(`Failed to fetch all markets ${err}`);
321
+ throw err;
322
+ }
323
+ }
324
+ /**
325
+ * Post a new job to the marketplace
326
+ * @param params Parameters for listing a job
327
+ * @returns The transaction signature
328
+ */
329
+ async post(params) {
330
+ if (!this.sdk.config.wallet) {
331
+ throw new index_js_1.NosanaError('No wallet found', index_js_1.ErrorCodes.NO_WALLET);
332
+ }
333
+ const jobKey = await (0, gill_1.generateKeyPairSigner)();
334
+ const runKey = await (0, gill_1.generateKeyPairSigner)();
335
+ const [associatedTokenAddress] = await (0, token_1.findAssociatedTokenPda)({
336
+ mint: this.sdk.config.programs.nosTokenAddress,
337
+ owner: this.sdk.config.wallet.signer.address,
338
+ tokenProgram: token_1.TOKEN_PROGRAM_ADDRESS
339
+ });
340
+ try {
341
+ const staticAccounts = await this.getStaticAccounts();
342
+ // Create the list instruction
343
+ const instruction = this.client.getListInstruction({
344
+ job: jobKey,
345
+ market: params.market,
346
+ run: runKey,
347
+ user: associatedTokenAddress,
348
+ vault: await this.sdk.solana.pda([
349
+ params.market,
350
+ this.sdk.config.programs.nosTokenAddress,
351
+ ], staticAccounts.jobsProgram),
352
+ payer: this.sdk.config.wallet.signer,
353
+ rewardsReflection: staticAccounts.rewardsReflection,
354
+ rewardsVault: staticAccounts.rewardsVault,
355
+ authority: this.sdk.config.wallet.signer,
356
+ rewardsProgram: staticAccounts.rewardsProgram,
357
+ ipfsJob: bs58_1.default.decode(params.ipfsHash).subarray(2),
358
+ timeout: params.timeout
359
+ });
360
+ if (params.instructionOnly)
361
+ return instruction;
362
+ // Create the transaction
363
+ const transaction = (0, gill_1.createTransaction)({
364
+ instructions: [instruction],
365
+ feePayer: this.sdk.config.wallet.signer,
366
+ latestBlockhash: await this.sdk.solana.getLatestBlockhash(),
367
+ version: 0,
368
+ });
369
+ // Sign the transaction with all required signers
370
+ const signedTransaction = await (0, gill_1.signTransactionMessageWithSigners)(transaction);
371
+ // Get the transaction signature for logging
372
+ const signature = (0, gill_1.getSignatureFromTransaction)(signedTransaction);
373
+ // Log the transaction explorer link
374
+ const explorerLink = (0, gill_1.getExplorerLink)({
375
+ cluster: this.sdk.config.solana.cluster,
376
+ transaction: signature
377
+ });
378
+ this.sdk.logger.info(`Sending list job transaction: ${explorerLink}`);
379
+ // Send and confirm the transaction
380
+ await this.sdk.solana.sendAndConfirmTransaction(signedTransaction);
381
+ this.sdk.logger.info("Job listing transaction confirmed!");
382
+ return signature;
383
+ }
384
+ catch (err) {
385
+ this.sdk.logger.error(`Failed to list job: ${err instanceof Error ? err.message : String(err)}`);
386
+ throw new Error(`Failed to list job: ${err instanceof Error ? err.message : String(err)}`);
387
+ }
388
+ }
389
+ /**
390
+ * Monitor program account updates using callback functions
391
+ * Uses WebSocket subscriptions with automatic restart on failure
392
+ *
393
+ * @example
394
+ * ```typescript
395
+ * // Example: Monitor job accounts and save to file
396
+ * const stopMonitoring = await jobsProgram.monitor({
397
+ * onJobAccount: async (jobAccount) => {
398
+ * console.log('Job updated:', jobAccount.address.toString());
399
+ * // Save to database, file, or process as needed
400
+ * },
401
+ * onRunAccount: async (runAccount) => {
402
+ * console.log('Run updated:', runAccount.address.toString());
403
+ * },
404
+ * onError: async (error, accountType) => {
405
+ * console.error('Error processing account:', error, accountType);
406
+ * }
407
+ * });
408
+ *
409
+ * // Stop monitoring when done
410
+ * stopMonitoring();
411
+ * ```
412
+ *
413
+ * @param options Configuration options for monitoring
414
+ * @returns A function to stop monitoring
415
+ */
416
+ async monitor(options = {}) {
417
+ const { onJobAccount, onMarketAccount, onRunAccount, onError } = options;
418
+ const programId = this.getProgramId();
419
+ try {
420
+ this.sdk.logger.info(`Starting to monitor job program account updates for program: ${programId}`);
421
+ let abortController = null;
422
+ let isMonitoring = true;
423
+ // Function to stop all monitoring
424
+ const stopMonitoring = () => {
425
+ isMonitoring = false;
426
+ if (abortController) {
427
+ abortController.abort();
428
+ }
429
+ this.sdk.logger.info(`Stopped monitoring job program account updates`);
430
+ };
431
+ // Function to start/restart subscription with retry logic
432
+ const startSubscription = async () => {
433
+ while (isMonitoring) {
434
+ try {
435
+ this.sdk.logger.info('Attempting to establish WebSocket subscription...');
436
+ abortController = new AbortController();
437
+ const subscriptionIterable = await this.setupSubscription(abortController);
438
+ this.sdk.logger.info('Successfully established WebSocket subscription');
439
+ // Start processing subscription notifications
440
+ await this.processSubscriptionNotifications(subscriptionIterable, { onJobAccount, onMarketAccount, onRunAccount, onError }, () => isMonitoring);
441
+ }
442
+ catch (error) {
443
+ this.sdk.logger.warn(`WebSocket subscription failed: ${error}`);
444
+ // Clean up current subscription
445
+ if (abortController) {
446
+ abortController.abort();
447
+ abortController = null;
448
+ }
449
+ if (isMonitoring) {
450
+ this.sdk.logger.info('Retrying WebSocket subscription in 5 seconds...');
451
+ await new Promise(resolve => setTimeout(resolve, 5000));
452
+ }
453
+ }
454
+ }
455
+ };
456
+ // Start the subscription loop
457
+ startSubscription().catch(error => {
458
+ this.sdk.logger.error(`Failed to start subscription loop: ${error}`);
459
+ });
460
+ this.sdk.logger.info(`Successfully started monitoring job program account updates`);
461
+ return stopMonitoring;
462
+ }
463
+ catch (error) {
464
+ this.sdk.logger.error(`Failed to start monitoring job program accounts: ${error}`);
465
+ throw new index_js_1.NosanaError('Failed to start monitoring job program accounts', index_js_1.ErrorCodes.RPC_ERROR, error);
466
+ }
467
+ }
468
+ /**
469
+ * Set up WebSocket subscription for program notifications
470
+ */
471
+ async setupSubscription(abortController) {
472
+ try {
473
+ // Set up the subscription using the correct API pattern
474
+ const subscriptionIterable = await this.sdk.solana.rpcSubscriptions
475
+ .programNotifications(this.getProgramId(), { encoding: 'base64' })
476
+ .subscribe({ abortSignal: abortController.signal });
477
+ return subscriptionIterable;
478
+ }
479
+ catch (error) {
480
+ throw new Error(`Failed to setup subscription: ${error}`);
481
+ }
482
+ }
483
+ /**
484
+ * Process subscription notifications
485
+ */
486
+ async processSubscriptionNotifications(notificationIterable, options, isMonitoring) {
487
+ try {
488
+ for await (const notification of notificationIterable) {
489
+ // Check if monitoring should continue
490
+ if (!isMonitoring()) {
491
+ this.sdk.logger.info('Monitoring stopped, exiting subscription processing');
492
+ break;
493
+ }
494
+ try {
495
+ const { value } = notification;
496
+ await this.handleAccountUpdate(value, options, isMonitoring);
497
+ }
498
+ catch (error) {
499
+ this.sdk.logger.error(`Error handling account update notification: ${error}`);
500
+ if (options.onError) {
501
+ try {
502
+ await options.onError(error instanceof Error ? error : new Error(String(error)));
503
+ }
504
+ catch (callbackError) {
505
+ this.sdk.logger.error(`Error in onError callback: ${callbackError}`);
506
+ }
507
+ }
508
+ }
509
+ }
510
+ }
511
+ catch (error) {
512
+ this.sdk.logger.error(`Subscription error: ${error}`);
513
+ // Throw the error so the calling function can restart the subscription
514
+ throw error;
515
+ }
516
+ }
517
+ transformJobAccount(jobAccount) {
518
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
519
+ const { discriminator: _, ...jobAccountData } = jobAccount.data;
520
+ return {
521
+ address: jobAccount.address,
522
+ ...(0, index_js_2.convertBigIntToNumber)(jobAccountData),
523
+ ipfsJob: IPFS_js_1.IPFS.solHashToIpfsHash(jobAccountData.ipfsJob),
524
+ ipfsResult: IPFS_js_1.IPFS.solHashToIpfsHash(jobAccountData.ipfsResult),
525
+ };
526
+ }
527
+ transformRunAccount(runAccount) {
528
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
529
+ const { discriminator: _, ...runAccountData } = runAccount.data;
530
+ return {
531
+ address: runAccount.address,
532
+ ...(0, index_js_2.convertBigIntToNumber)(runAccountData),
533
+ };
534
+ }
535
+ transformMarketAccount(marketAccount) {
536
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
537
+ const { discriminator: _, ...marketAccountData } = marketAccount.data;
538
+ return {
539
+ address: marketAccount.address,
540
+ ...(0, index_js_2.convertBigIntToNumber)(marketAccountData),
541
+ };
542
+ }
543
+ /**
544
+ * Handle account update using callback functions
545
+ */
546
+ async handleAccountUpdate(accountData, options, isMonitoring) {
547
+ try {
548
+ const { account, pubkey } = accountData;
549
+ const encodedAccount = (0, gill_1.parseBase64RpcAccount)(pubkey, account);
550
+ const accountType = programClient.identifyNosanaJobsAccount(encodedAccount);
551
+ switch (accountType) {
552
+ case programClient.NosanaJobsAccount.JobAccount:
553
+ const jobAccount = programClient.decodeJobAccount(encodedAccount);
554
+ await this.handleJobAccount(jobAccount, options.onJobAccount, isMonitoring);
555
+ break;
556
+ case programClient.NosanaJobsAccount.MarketAccount:
557
+ const marketAccount = programClient.decodeMarketAccount(encodedAccount);
558
+ await this.handleMarketAccount(marketAccount, options.onMarketAccount, isMonitoring);
559
+ break;
560
+ case programClient.NosanaJobsAccount.RunAccount:
561
+ const runAccount = programClient.decodeRunAccount(encodedAccount);
562
+ await this.handleRunAccount(runAccount, options.onRunAccount, isMonitoring);
563
+ break;
564
+ default:
565
+ this.sdk.logger.error(`No support yet for account type: ${accountType}`);
566
+ return;
567
+ }
568
+ }
569
+ catch (error) {
570
+ this.sdk.logger.error(`Error in handleAccountUpdate: ${error}`);
571
+ }
572
+ }
573
+ async handleJobAccount(jobAccount, onJobAccount, _isMonitoring) {
574
+ if (onJobAccount) {
575
+ try {
576
+ await onJobAccount(this.transformJobAccount(jobAccount));
577
+ }
578
+ catch (error) {
579
+ this.sdk.logger.error(`Error in onJobAccount callback: ${error}`);
580
+ throw error;
581
+ }
582
+ }
583
+ this.sdk.logger.debug(`Processed job account ${jobAccount.address.toString()}`);
584
+ }
585
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
586
+ async handleMarketAccount(marketAccount, onMarketAccount, _isMonitoring) {
587
+ if (onMarketAccount) {
588
+ try {
589
+ await onMarketAccount(this.transformMarketAccount(marketAccount));
590
+ }
591
+ catch (error) {
592
+ this.sdk.logger.error(`Error in onMarketAccount callback: ${error}`);
593
+ throw error;
594
+ }
595
+ }
596
+ this.sdk.logger.debug(`Processed market account ${marketAccount.address.toString()}`);
597
+ }
598
+ async handleRunAccount(runAccount, onRunAccount, _isMonitoring) {
599
+ if (onRunAccount) {
600
+ try {
601
+ await onRunAccount(this.transformRunAccount(runAccount));
602
+ }
603
+ catch (error) {
604
+ this.sdk.logger.error(`Error in onRunAccount callback: ${error}`);
605
+ throw error;
606
+ }
607
+ }
608
+ this.sdk.logger.debug(`Processed run account ${runAccount.address.toString()}`);
609
+ }
610
+ }
611
+ exports.JobsProgram = JobsProgram;
@@ -0,0 +1,15 @@
1
+ import { Address, SolanaClient } from 'gill';
2
+ import { NosanaClient } from '../index.js';
3
+ export declare class SolanaUtils {
4
+ private readonly sdk;
5
+ readonly rpc: SolanaClient["rpc"];
6
+ readonly rpcSubscriptions: SolanaClient["rpcSubscriptions"];
7
+ readonly sendAndConfirmTransaction: SolanaClient["sendAndConfirmTransaction"];
8
+ constructor(sdk: NosanaClient);
9
+ pda(seeds: Array<Address | string>, programId: Address): Promise<Address>;
10
+ getBalance(addressStr: string | Address): Promise<bigint>;
11
+ getLatestBlockhash(): Promise<Readonly<{
12
+ blockhash: import("gill").Blockhash;
13
+ lastValidBlockHeight: bigint;
14
+ }>>;
15
+ }
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SolanaUtils = void 0;
4
+ const gill_1 = require("gill");
5
+ const NosanaError_js_1 = require("../errors/NosanaError.js");
6
+ class SolanaUtils {
7
+ constructor(sdk) {
8
+ this.sdk = sdk;
9
+ const rpcEndpoint = this.sdk.config.solana.rpcEndpoint;
10
+ if (!rpcEndpoint)
11
+ throw new NosanaError_js_1.NosanaError('RPC URL is required', NosanaError_js_1.ErrorCodes.INVALID_CONFIG);
12
+ const { rpc, rpcSubscriptions, sendAndConfirmTransaction } = (0, gill_1.createSolanaClient)({ urlOrMoniker: rpcEndpoint });
13
+ this.rpc = rpc;
14
+ this.rpcSubscriptions = rpcSubscriptions;
15
+ this.sendAndConfirmTransaction = sendAndConfirmTransaction;
16
+ }
17
+ async pda(seeds, programId) {
18
+ const addressEncoder = (0, gill_1.getAddressEncoder)();
19
+ const [pda] = await (0, gill_1.getProgramDerivedAddress)({
20
+ programAddress: programId,
21
+ seeds: seeds.map(seed => typeof seed !== 'string' ? addressEncoder.encode(seed) : seed),
22
+ });
23
+ return pda;
24
+ }
25
+ async getBalance(addressStr) {
26
+ try {
27
+ this.sdk.logger.debug(`Getting balance for address: ${addressStr}`);
28
+ const addr = (0, gill_1.address)(addressStr);
29
+ const balance = await this.rpc.getBalance(addr).send();
30
+ return balance.value;
31
+ }
32
+ catch (error) {
33
+ this.sdk.logger.error(`Failed to get balance: ${error}`);
34
+ throw new NosanaError_js_1.NosanaError('Failed to get balance', NosanaError_js_1.ErrorCodes.RPC_ERROR, error);
35
+ }
36
+ }
37
+ async getLatestBlockhash() {
38
+ try {
39
+ const { value: blockhash } = await this.rpc.getLatestBlockhash().send();
40
+ return blockhash;
41
+ }
42
+ catch (error) {
43
+ this.sdk.logger.error(`Failed to get latest blockhash: ${error}`);
44
+ throw new NosanaError_js_1.NosanaError('Failed to get latest blockhash', NosanaError_js_1.ErrorCodes.RPC_ERROR, error);
45
+ }
46
+ }
47
+ }
48
+ exports.SolanaUtils = SolanaUtils;
@@ -0,0 +1,19 @@
1
+ import { ReadonlyUint8Array } from 'gill';
2
+ /**
3
+ * Type helper to convert bigint properties to number
4
+ */
5
+ export type ConvertBigIntToNumber<T> = {
6
+ [K in keyof T]: T[K] extends bigint ? number : T[K];
7
+ };
8
+ /**
9
+ * Type helper to convert bigint to number and ReadonlyUint8Array to string
10
+ */
11
+ export type ConvertTypesForDb<T> = {
12
+ [K in keyof T]: T[K] extends bigint ? number : T[K] extends ReadonlyUint8Array ? string : T[K];
13
+ };
14
+ /**
15
+ * Helper function to convert bigint values to numbers in an object
16
+ * @param obj Object that may contain bigint values
17
+ * @returns Object with all bigint values converted to numbers
18
+ */
19
+ export declare function convertBigIntToNumber<T extends Record<string, any>>(obj: T): ConvertBigIntToNumber<T>;