@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.
- package/.gitlab-ci.yml +21 -0
- package/README.md +159 -0
- package/dist/config/defaultConfigs.d.ts +2 -0
- package/dist/config/defaultConfigs.js +47 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.js +19 -0
- package/dist/config/types.d.ts +43 -0
- package/dist/config/types.js +16 -0
- package/dist/config/utils.d.ts +3 -0
- package/dist/config/utils.js +35 -0
- package/dist/errors/NosanaError.d.ts +16 -0
- package/dist/errors/NosanaError.js +22 -0
- package/dist/generated_clients/jobs/accounts/index.d.ts +10 -0
- package/dist/generated_clients/jobs/accounts/index.js +26 -0
- package/dist/generated_clients/jobs/accounts/jobAccount.d.ts +47 -0
- package/dist/generated_clients/jobs/accounts/jobAccount.js +86 -0
- package/dist/generated_clients/jobs/accounts/marketAccount.d.ts +46 -0
- package/dist/generated_clients/jobs/accounts/marketAccount.js +82 -0
- package/dist/generated_clients/jobs/accounts/runAccount.d.ts +35 -0
- package/dist/generated_clients/jobs/accounts/runAccount.js +74 -0
- package/dist/generated_clients/jobs/errors/index.d.ts +8 -0
- package/dist/generated_clients/jobs/errors/index.js +24 -0
- package/dist/generated_clients/jobs/errors/nosanaJobs.d.ts +57 -0
- package/dist/generated_clients/jobs/errors/nosanaJobs.js +85 -0
- package/dist/generated_clients/jobs/index.d.ts +12 -0
- package/dist/generated_clients/jobs/index.js +28 -0
- package/dist/generated_clients/jobs/instructions/claim.d.ts +60 -0
- package/dist/generated_clients/jobs/instructions/claim.js +102 -0
- package/dist/generated_clients/jobs/instructions/clean.d.ts +42 -0
- package/dist/generated_clients/jobs/instructions/clean.js +79 -0
- package/dist/generated_clients/jobs/instructions/cleanAdmin.d.ts +42 -0
- package/dist/generated_clients/jobs/instructions/cleanAdmin.js +79 -0
- package/dist/generated_clients/jobs/instructions/close.d.ts +48 -0
- package/dist/generated_clients/jobs/instructions/close.js +90 -0
- package/dist/generated_clients/jobs/instructions/closeAdmin.d.ts +48 -0
- package/dist/generated_clients/jobs/instructions/closeAdmin.js +90 -0
- package/dist/generated_clients/jobs/instructions/complete.d.ts +43 -0
- package/dist/generated_clients/jobs/instructions/complete.js +82 -0
- package/dist/generated_clients/jobs/instructions/delist.d.ts +54 -0
- package/dist/generated_clients/jobs/instructions/delist.js +96 -0
- package/dist/generated_clients/jobs/instructions/end.d.ts +60 -0
- package/dist/generated_clients/jobs/instructions/end.js +102 -0
- package/dist/generated_clients/jobs/instructions/extend.d.ts +64 -0
- package/dist/generated_clients/jobs/instructions/extend.js +111 -0
- package/dist/generated_clients/jobs/instructions/finish.d.ts +67 -0
- package/dist/generated_clients/jobs/instructions/finish.js +111 -0
- package/dist/generated_clients/jobs/instructions/index.d.ts +25 -0
- package/dist/generated_clients/jobs/instructions/index.js +41 -0
- package/dist/generated_clients/jobs/instructions/list.d.ts +76 -0
- package/dist/generated_clients/jobs/instructions/list.js +126 -0
- package/dist/generated_clients/jobs/instructions/open.d.ts +73 -0
- package/dist/generated_clients/jobs/instructions/open.js +121 -0
- package/dist/generated_clients/jobs/instructions/quit.d.ts +45 -0
- package/dist/generated_clients/jobs/instructions/quit.js +82 -0
- package/dist/generated_clients/jobs/instructions/quitAdmin.d.ts +42 -0
- package/dist/generated_clients/jobs/instructions/quitAdmin.js +79 -0
- package/dist/generated_clients/jobs/instructions/recover.d.ts +54 -0
- package/dist/generated_clients/jobs/instructions/recover.js +96 -0
- package/dist/generated_clients/jobs/instructions/stop.d.ts +42 -0
- package/dist/generated_clients/jobs/instructions/stop.js +79 -0
- package/dist/generated_clients/jobs/instructions/update.d.ts +58 -0
- package/dist/generated_clients/jobs/instructions/update.js +93 -0
- package/dist/generated_clients/jobs/instructions/work.d.ts +57 -0
- package/dist/generated_clients/jobs/instructions/work.js +99 -0
- package/dist/generated_clients/jobs/programs/index.d.ts +8 -0
- package/dist/generated_clients/jobs/programs/index.js +24 -0
- package/dist/generated_clients/jobs/programs/nosanaJobs.d.ts +78 -0
- package/dist/generated_clients/jobs/programs/nosanaJobs.js +112 -0
- package/dist/generated_clients/jobs/shared/index.d.ts +49 -0
- package/dist/generated_clients/jobs/shared/index.js +94 -0
- package/dist/generated_clients/jobs/types/index.d.ts +10 -0
- package/dist/generated_clients/jobs/types/index.js +26 -0
- package/dist/generated_clients/jobs/types/jobState.d.ts +18 -0
- package/dist/generated_clients/jobs/types/jobState.js +30 -0
- package/dist/generated_clients/jobs/types/jobType.d.ts +21 -0
- package/dist/generated_clients/jobs/types/jobType.js +33 -0
- package/dist/generated_clients/jobs/types/queueType.d.ts +18 -0
- package/dist/generated_clients/jobs/types/queueType.js +30 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +43 -0
- package/dist/ipfs/IPFS.d.ts +19 -0
- package/dist/ipfs/IPFS.js +47 -0
- package/dist/logger/Logger.d.ts +24 -0
- package/dist/logger/Logger.js +63 -0
- package/dist/programs/BaseProgram.d.ts +20 -0
- package/dist/programs/BaseProgram.js +39 -0
- package/dist/programs/JobsProgram.d.ts +118 -0
- package/dist/programs/JobsProgram.js +611 -0
- package/dist/solana/SolanaUtils.d.ts +15 -0
- package/dist/solana/SolanaUtils.js +48 -0
- package/dist/utils/index.d.ts +19 -0
- package/dist/utils/index.js +17 -0
- package/examples/node/monitor.ts +143 -0
- package/examples/node/package-lock.json +245 -0
- package/examples/node/package.json +16 -0
- package/examples/node/retrieve.ts +18 -0
- package/package.json +47 -0
- 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>;
|