lightnode-sdk 0.4.9 → 0.5.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.
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { LightNode, modelStatsCsv, workerStatsCsv, workerJobsCsv, runInferenceWithKey, isStalledWorker } from "./index.js";
2
+ import { LightNode, modelStatsCsv, workerStatsCsv, workerJobsCsv, runInferenceWithKey, isStalledWorker, workerPreflight, workerWatch, BRIDGE_ROUTE, DAO, DAO_ADDRESSES } from "./index.js";
3
3
  import { addInference, addAnalyticsDashboard, addNftMint, addChat, addAgent } from "./add.js";
4
4
  import { createPublicClient, http, parseEther } from "viem";
5
5
  import { privateKeyToAccount, generatePrivateKey } from "viem/accounts";
@@ -32,12 +32,24 @@ Read-only network commands (no key):
32
32
  network network summary (workers, jobs, models, earnings)
33
33
  models registered models + per-job fee
34
34
  worker <addr> a worker: on-chain registration + recent jobs
35
+ worker watch <addr> poll worker status, print event on change
36
+ ([--interval 30] [--stale 90])
35
37
  jobs <addr> [--csv] one worker's job history (table or CSV)
38
+ job <jobId> one job's status (category, refundable, worker, timings)
36
39
  registered <addr> true | false | null (read from chain events)
37
40
  fee [model] on-chain inference fee (default llama3-8b)
38
41
  analytics [--csv] per-model performance (completion, p50/p95, incomplete)
39
42
  reliability [--csv] per-worker reliability, busiest first
40
43
 
44
+ Preflight (needs PRIVATE_KEY in env):
45
+ worker preflight run one real test inference, print verdict + timings
46
+ ([--key 0x...] [--model llama3-8b] [--deadline 60])
47
+
48
+ Ecosystem (read-only):
49
+ bridge addresses print bridge route (Ethereum <-> LightChain) addresses
50
+ dao addresses print LCAI Governor + Timelock + Treasury addresses
51
+ dao config print voting delay / period / threshold (live read)
52
+
41
53
  Scaffold templates into the current project:
42
54
  add inference end-to-end encrypted inference route/script
43
55
  add chat chat-style UI with conversation history
@@ -145,11 +157,63 @@ async function main() {
145
157
  break;
146
158
  }
147
159
  case "worker": {
148
- const addr = positionals[1] ?? die("usage: lightnode worker <address> [--net testnet]");
160
+ // Two sub-shapes: `lightnode worker <addr>` (one-shot status) and
161
+ // `lightnode worker watch <addr>` (long-running event stream) and
162
+ // `lightnode worker preflight` (submit a test inference).
163
+ const sub = positionals[1];
164
+ if (sub === "watch") {
165
+ const addr = positionals[2] ?? die("usage: lightnode worker watch <address> [--interval 30] [--stale 90]");
166
+ const intervalSec = Number(flag("--interval") ?? "30");
167
+ const staleSecs = Number(flag("--stale") ?? "90");
168
+ const handle = workerWatch(ln, addr, { intervalMs: intervalSec * 1000, staleSecs });
169
+ process.on("SIGINT", () => {
170
+ handle.stop();
171
+ process.exit(0);
172
+ });
173
+ for await (const event of handle.events) {
174
+ console.log(JSON.stringify(event));
175
+ }
176
+ break;
177
+ }
178
+ if (sub === "preflight") {
179
+ const privateKey = pickKey();
180
+ const model = flag("--model") ?? "llama3-8b";
181
+ const deadlineMs = Number(flag("--deadline") ?? "60") * 1000;
182
+ console.error(`> preflight against ${net} (model=${model}, deadline=${deadlineMs / 1000}s)...`);
183
+ const r = await workerPreflight({ network: net, privateKey, model, deadlineMs });
184
+ const explorer = ln.network.explorer;
185
+ console.log(JSON.stringify({
186
+ verdict: r.verdict,
187
+ elapsedSec: Math.round(r.elapsedMs / 100) / 10,
188
+ worker: r.worker,
189
+ summary: r.summary,
190
+ txs: {
191
+ createSession: r.txs.createSession ? `${explorer}/tx/${r.txs.createSession}` : null,
192
+ submitJob: r.txs.submitJob ? `${explorer}/tx/${r.txs.submitJob}` : null,
193
+ jobCompleted: r.txs.jobCompleted ? `${explorer}/tx/${r.txs.jobCompleted}` : null,
194
+ },
195
+ error: r.error,
196
+ }, null, 2));
197
+ if (r.verdict === "failed" || r.verdict === "stalled")
198
+ process.exit(1);
199
+ break;
200
+ }
201
+ // Default: one-shot worker summary by address.
202
+ const addr = sub ?? die("usage: lightnode worker <address|watch|preflight> [...]");
149
203
  const [w, registered, jobs] = await Promise.all([ln.getWorker(addr), ln.isRegistered(addr), ln.getWorkerJobs(addr, 5)]);
150
204
  console.log(JSON.stringify({ onchainRegistered: registered, worker: w, recentJobs: jobs.map((j) => ({ id: j.id, state: j.state })) }, null, 2));
151
205
  break;
152
206
  }
207
+ case "job": {
208
+ const id = positionals[1] ?? die("usage: lightnode job <jobId> [--net testnet]");
209
+ const status = await ln.getJobStatus(id);
210
+ if (!status) {
211
+ console.log(JSON.stringify({ jobId: id, status: "not-indexed" }, null, 2));
212
+ break;
213
+ }
214
+ console.log(JSON.stringify(status, null, 2));
215
+ break;
216
+ }
153
217
  case "jobs": {
154
218
  const addr = positionals[1] ?? die("usage: lightnode jobs <address> [--csv] [--net testnet]");
155
219
  const jobs = await ln.getWorkerJobs(addr, 100);
@@ -196,6 +260,45 @@ async function main() {
196
260
  }
197
261
  break;
198
262
  }
263
+ case "bridge": {
264
+ const sub = positionals[1];
265
+ if (sub === "addresses") {
266
+ console.log(JSON.stringify(BRIDGE_ROUTE, null, 2));
267
+ break;
268
+ }
269
+ die("usage: lightnode bridge <addresses>");
270
+ break;
271
+ }
272
+ case "dao": {
273
+ const sub = positionals[1];
274
+ if (sub === "addresses") {
275
+ console.log(JSON.stringify(DAO_ADDRESSES.ethereum, null, 2));
276
+ break;
277
+ }
278
+ if (sub === "config") {
279
+ // Live read against Ethereum mainnet. We use viem's HTTP transport
280
+ // via a minimal inline client (no ethers dep). This is the only
281
+ // ecosystem read that needs a live RPC, so we wire it lazily.
282
+ const { createPublicClient, http } = await import("viem");
283
+ const ethRpc = flag("--rpc") ?? "https://eth.llamarpc.com";
284
+ const pub = createPublicClient({ transport: http(ethRpc) });
285
+ // The DAO ctor accepts a structurally-typed MinimalPublicClient; viem's
286
+ // PublicClient satisfies it. The unknown cast is the standard SDK pattern
287
+ // for keeping the public API free of viem generic noise.
288
+ const dao = new DAO(pub, "ethereum");
289
+ const cfg = await dao.config();
290
+ console.log(JSON.stringify({
291
+ votingDelayBlocks: cfg.votingDelayBlocks.toString(),
292
+ votingPeriodBlocks: cfg.votingPeriodBlocks.toString(),
293
+ votingPeriodSecs: cfg.votingPeriodSecs,
294
+ proposalThresholdLcai: Number(cfg.proposalThresholdWei) / 1e18,
295
+ addresses: dao.addresses,
296
+ }, null, 2));
297
+ break;
298
+ }
299
+ die("usage: lightnode dao <addresses|config> [--rpc <ethereum-rpc>]");
300
+ break;
301
+ }
199
302
  case "add": {
200
303
  const sub = positionals[1];
201
304
  const template = flag("--template") ?? "auto";
package/dist/dao.d.ts ADDED
@@ -0,0 +1,439 @@
1
+ /**
2
+ * DAO SDK: typed wrapper around the LCAI Governor (OpenZeppelin Governor v5)
3
+ * on Ethereum mainnet. Addresses extracted from
4
+ * `lightchain-protocol/LCAI-dao-frontend/config/index.ts`.
5
+ *
6
+ * Governance is currently Ethereum-side (chain 1). Voting on LCAI proposals
7
+ * happens via the LCAI ERC-20 wrapped as IVotes (LCAIBallots). Execution
8
+ * goes through LCAITimeLock with the timelock controller managing actual
9
+ * calldata dispatch.
10
+ *
11
+ * Voting parameters (hard-coded in LCAIGovernor.sol constructor):
12
+ * - votingDelay = 7,200 blocks (~1 day at 12s)
13
+ * - votingPeriod = 100,800 blocks (~14 days at 12s)
14
+ * - proposalThreshold = 140,000 LCAI (votes required to create a proposal)
15
+ * - quorum = 3% of total supply (3-15 by admin)
16
+ *
17
+ * This module covers the OZ Governor v5 surface: state machine, propose,
18
+ * castVote, queue, execute. Plus convenience reads of the constants.
19
+ */
20
+ export type DaoChain = "ethereum";
21
+ export interface DaoAddresses {
22
+ chainId: number;
23
+ /** OZ Governor contract. */
24
+ governor: `0x${string}`;
25
+ /** Timelock controller. queue/execute dispatch through this. */
26
+ timelock: `0x${string}`;
27
+ /** ERC-20 wrapped as IVotes; this is what users delegate / hold to vote. */
28
+ ballots: `0x${string}`;
29
+ /** LCAI ERC-20 (the underlying governance token). */
30
+ token: `0x${string}`;
31
+ /** Treasury contract holding DAO funds. */
32
+ treasury: `0x${string}`;
33
+ explorer: string;
34
+ }
35
+ /** Confirmed Ethereum mainnet addresses (chain 1). */
36
+ export declare const DAO_ADDRESSES: Record<DaoChain, DaoAddresses>;
37
+ /**
38
+ * The 8-state OZ Governor v5 enum. The string label is what most builders
39
+ * will want to surface in a UI.
40
+ */
41
+ export declare enum ProposalState {
42
+ Pending = 0,
43
+ Active = 1,
44
+ Canceled = 2,
45
+ Defeated = 3,
46
+ Succeeded = 4,
47
+ Queued = 5,
48
+ Expired = 6,
49
+ Executed = 7
50
+ }
51
+ export declare const PROPOSAL_STATE_LABEL: Record<ProposalState, string>;
52
+ /** Vote support values. Maps to OZ's GovernorCountingSimple. */
53
+ export declare enum VoteSupport {
54
+ Against = 0,
55
+ For = 1,
56
+ Abstain = 2
57
+ }
58
+ /** OZ Governor v5 ABI (subset). */
59
+ export declare const GOVERNOR_ABI: readonly [{
60
+ readonly name: "propose";
61
+ readonly type: "function";
62
+ readonly stateMutability: "nonpayable";
63
+ readonly inputs: readonly [{
64
+ readonly type: "address[]";
65
+ readonly name: "targets";
66
+ }, {
67
+ readonly type: "uint256[]";
68
+ readonly name: "values";
69
+ }, {
70
+ readonly type: "bytes[]";
71
+ readonly name: "calldatas";
72
+ }, {
73
+ readonly type: "string";
74
+ readonly name: "description";
75
+ }];
76
+ readonly outputs: readonly [{
77
+ readonly type: "uint256";
78
+ readonly name: "proposalId";
79
+ }];
80
+ }, {
81
+ readonly name: "castVote";
82
+ readonly type: "function";
83
+ readonly stateMutability: "nonpayable";
84
+ readonly inputs: readonly [{
85
+ readonly type: "uint256";
86
+ readonly name: "proposalId";
87
+ }, {
88
+ readonly type: "uint8";
89
+ readonly name: "support";
90
+ }];
91
+ readonly outputs: readonly [{
92
+ readonly type: "uint256";
93
+ }];
94
+ }, {
95
+ readonly name: "castVoteWithReason";
96
+ readonly type: "function";
97
+ readonly stateMutability: "nonpayable";
98
+ readonly inputs: readonly [{
99
+ readonly type: "uint256";
100
+ readonly name: "proposalId";
101
+ }, {
102
+ readonly type: "uint8";
103
+ readonly name: "support";
104
+ }, {
105
+ readonly type: "string";
106
+ readonly name: "reason";
107
+ }];
108
+ readonly outputs: readonly [{
109
+ readonly type: "uint256";
110
+ }];
111
+ }, {
112
+ readonly name: "queue";
113
+ readonly type: "function";
114
+ readonly stateMutability: "nonpayable";
115
+ readonly inputs: readonly [{
116
+ readonly type: "address[]";
117
+ readonly name: "targets";
118
+ }, {
119
+ readonly type: "uint256[]";
120
+ readonly name: "values";
121
+ }, {
122
+ readonly type: "bytes[]";
123
+ readonly name: "calldatas";
124
+ }, {
125
+ readonly type: "bytes32";
126
+ readonly name: "descriptionHash";
127
+ }];
128
+ readonly outputs: readonly [{
129
+ readonly type: "uint256";
130
+ }];
131
+ }, {
132
+ readonly name: "execute";
133
+ readonly type: "function";
134
+ readonly stateMutability: "payable";
135
+ readonly inputs: readonly [{
136
+ readonly type: "address[]";
137
+ readonly name: "targets";
138
+ }, {
139
+ readonly type: "uint256[]";
140
+ readonly name: "values";
141
+ }, {
142
+ readonly type: "bytes[]";
143
+ readonly name: "calldatas";
144
+ }, {
145
+ readonly type: "bytes32";
146
+ readonly name: "descriptionHash";
147
+ }];
148
+ readonly outputs: readonly [{
149
+ readonly type: "uint256";
150
+ }];
151
+ }, {
152
+ readonly name: "state";
153
+ readonly type: "function";
154
+ readonly stateMutability: "view";
155
+ readonly inputs: readonly [{
156
+ readonly type: "uint256";
157
+ readonly name: "proposalId";
158
+ }];
159
+ readonly outputs: readonly [{
160
+ readonly type: "uint8";
161
+ }];
162
+ }, {
163
+ readonly name: "hashProposal";
164
+ readonly type: "function";
165
+ readonly stateMutability: "pure";
166
+ readonly inputs: readonly [{
167
+ readonly type: "address[]";
168
+ readonly name: "targets";
169
+ }, {
170
+ readonly type: "uint256[]";
171
+ readonly name: "values";
172
+ }, {
173
+ readonly type: "bytes[]";
174
+ readonly name: "calldatas";
175
+ }, {
176
+ readonly type: "bytes32";
177
+ readonly name: "descriptionHash";
178
+ }];
179
+ readonly outputs: readonly [{
180
+ readonly type: "uint256";
181
+ }];
182
+ }, {
183
+ readonly name: "votingDelay";
184
+ readonly type: "function";
185
+ readonly stateMutability: "view";
186
+ readonly inputs: readonly [];
187
+ readonly outputs: readonly [{
188
+ readonly type: "uint256";
189
+ }];
190
+ }, {
191
+ readonly name: "votingPeriod";
192
+ readonly type: "function";
193
+ readonly stateMutability: "view";
194
+ readonly inputs: readonly [];
195
+ readonly outputs: readonly [{
196
+ readonly type: "uint256";
197
+ }];
198
+ }, {
199
+ readonly name: "proposalThreshold";
200
+ readonly type: "function";
201
+ readonly stateMutability: "view";
202
+ readonly inputs: readonly [];
203
+ readonly outputs: readonly [{
204
+ readonly type: "uint256";
205
+ }];
206
+ }, {
207
+ readonly name: "quorum";
208
+ readonly type: "function";
209
+ readonly stateMutability: "view";
210
+ readonly inputs: readonly [{
211
+ readonly type: "uint256";
212
+ readonly name: "timepoint";
213
+ }];
214
+ readonly outputs: readonly [{
215
+ readonly type: "uint256";
216
+ }];
217
+ }, {
218
+ readonly name: "proposalVotes";
219
+ readonly type: "function";
220
+ readonly stateMutability: "view";
221
+ readonly inputs: readonly [{
222
+ readonly type: "uint256";
223
+ readonly name: "proposalId";
224
+ }];
225
+ readonly outputs: readonly [{
226
+ readonly type: "uint256";
227
+ readonly name: "againstVotes";
228
+ }, {
229
+ readonly type: "uint256";
230
+ readonly name: "forVotes";
231
+ }, {
232
+ readonly type: "uint256";
233
+ readonly name: "abstainVotes";
234
+ }];
235
+ }, {
236
+ readonly name: "proposalSnapshot";
237
+ readonly type: "function";
238
+ readonly stateMutability: "view";
239
+ readonly inputs: readonly [{
240
+ readonly type: "uint256";
241
+ readonly name: "proposalId";
242
+ }];
243
+ readonly outputs: readonly [{
244
+ readonly type: "uint256";
245
+ }];
246
+ }, {
247
+ readonly name: "proposalDeadline";
248
+ readonly type: "function";
249
+ readonly stateMutability: "view";
250
+ readonly inputs: readonly [{
251
+ readonly type: "uint256";
252
+ readonly name: "proposalId";
253
+ }];
254
+ readonly outputs: readonly [{
255
+ readonly type: "uint256";
256
+ }];
257
+ }, {
258
+ readonly name: "proposalProposer";
259
+ readonly type: "function";
260
+ readonly stateMutability: "view";
261
+ readonly inputs: readonly [{
262
+ readonly type: "uint256";
263
+ readonly name: "proposalId";
264
+ }];
265
+ readonly outputs: readonly [{
266
+ readonly type: "address";
267
+ }];
268
+ }, {
269
+ readonly name: "proposalEta";
270
+ readonly type: "function";
271
+ readonly stateMutability: "view";
272
+ readonly inputs: readonly [{
273
+ readonly type: "uint256";
274
+ readonly name: "proposalId";
275
+ }];
276
+ readonly outputs: readonly [{
277
+ readonly type: "uint256";
278
+ }];
279
+ }, {
280
+ readonly name: "getVotes";
281
+ readonly type: "function";
282
+ readonly stateMutability: "view";
283
+ readonly inputs: readonly [{
284
+ readonly type: "address";
285
+ readonly name: "account";
286
+ }, {
287
+ readonly type: "uint256";
288
+ readonly name: "timepoint";
289
+ }];
290
+ readonly outputs: readonly [{
291
+ readonly type: "uint256";
292
+ }];
293
+ }, {
294
+ readonly name: "hasVoted";
295
+ readonly type: "function";
296
+ readonly stateMutability: "view";
297
+ readonly inputs: readonly [{
298
+ readonly type: "uint256";
299
+ readonly name: "proposalId";
300
+ }, {
301
+ readonly type: "address";
302
+ readonly name: "account";
303
+ }];
304
+ readonly outputs: readonly [{
305
+ readonly type: "bool";
306
+ }];
307
+ }];
308
+ /** Minimal IVotes ABI for delegate + balance reads (LCAIBallots). */
309
+ export declare const VOTES_ABI: readonly [{
310
+ readonly name: "balanceOf";
311
+ readonly type: "function";
312
+ readonly stateMutability: "view";
313
+ readonly inputs: readonly [{
314
+ readonly type: "address";
315
+ }];
316
+ readonly outputs: readonly [{
317
+ readonly type: "uint256";
318
+ }];
319
+ }, {
320
+ readonly name: "getVotes";
321
+ readonly type: "function";
322
+ readonly stateMutability: "view";
323
+ readonly inputs: readonly [{
324
+ readonly type: "address";
325
+ }];
326
+ readonly outputs: readonly [{
327
+ readonly type: "uint256";
328
+ }];
329
+ }, {
330
+ readonly name: "delegates";
331
+ readonly type: "function";
332
+ readonly stateMutability: "view";
333
+ readonly inputs: readonly [{
334
+ readonly type: "address";
335
+ }];
336
+ readonly outputs: readonly [{
337
+ readonly type: "address";
338
+ }];
339
+ }, {
340
+ readonly name: "delegate";
341
+ readonly type: "function";
342
+ readonly stateMutability: "nonpayable";
343
+ readonly inputs: readonly [{
344
+ readonly type: "address";
345
+ readonly name: "delegatee";
346
+ }];
347
+ readonly outputs: readonly [{
348
+ readonly type: "bool";
349
+ }];
350
+ }];
351
+ interface MinimalPublicClient {
352
+ readContract: (args: {
353
+ address: `0x${string}`;
354
+ abi: readonly unknown[];
355
+ functionName: string;
356
+ args?: readonly unknown[];
357
+ }) => Promise<unknown>;
358
+ }
359
+ interface MinimalWalletClient {
360
+ writeContract: (args: {
361
+ address: `0x${string}`;
362
+ abi: readonly unknown[];
363
+ functionName: string;
364
+ args: readonly unknown[];
365
+ value?: bigint;
366
+ gas?: bigint;
367
+ }) => Promise<`0x${string}`>;
368
+ }
369
+ export interface ProposalSummary {
370
+ id: bigint;
371
+ state: ProposalState;
372
+ stateLabel: string;
373
+ proposer: `0x${string}` | null;
374
+ snapshot: bigint;
375
+ deadline: bigint;
376
+ eta: bigint;
377
+ votes: {
378
+ againstWei: bigint;
379
+ forWei: bigint;
380
+ abstainWei: bigint;
381
+ };
382
+ }
383
+ export interface DaoConfig {
384
+ votingDelayBlocks: bigint;
385
+ votingPeriodBlocks: bigint;
386
+ proposalThresholdWei: bigint;
387
+ /** Approx voting period in seconds, assuming 12s/block on Ethereum. */
388
+ votingPeriodSecs: number;
389
+ }
390
+ /**
391
+ * DAO client. Wraps reads (proposal state, config) + writes (propose, vote,
392
+ * queue, execute) on the Ethereum LCAIGovernor.
393
+ */
394
+ export declare class DAO {
395
+ private readonly publicClient;
396
+ private readonly walletClient?;
397
+ /** Addresses for the configured DAO chain. Currently only Ethereum mainnet. */
398
+ readonly addresses: DaoAddresses;
399
+ constructor(publicClient: MinimalPublicClient, chain?: DaoChain, walletClient?: MinimalWalletClient | undefined);
400
+ /** Current proposal state by id. */
401
+ state(proposalId: bigint): Promise<ProposalState>;
402
+ /** Full proposal summary by id. Aggregates state + votes + key blocks. */
403
+ proposal(proposalId: bigint): Promise<ProposalSummary>;
404
+ /** Whether `voter` has cast a vote on `proposalId`. */
405
+ hasVoted(proposalId: bigint, voter: `0x${string}`): Promise<boolean>;
406
+ /** Voting weight of `voter` at a specific block (use the proposal's `snapshot`). */
407
+ getVotes(voter: `0x${string}`, timepoint: bigint): Promise<bigint>;
408
+ /** Aggregated voting parameters of the LCAIGovernor. */
409
+ config(): Promise<DaoConfig>;
410
+ /** Quorum required at a given timepoint (in wei of voting weight). */
411
+ quorum(timepoint: bigint): Promise<bigint>;
412
+ /** Cast a For / Against / Abstain vote. Wallet must be the voter and have delegated their LCAI. */
413
+ castVote(proposalId: bigint, support: VoteSupport, reason?: string): Promise<`0x${string}`>;
414
+ /** Submit a new proposal. Wallet must hold at least `proposalThreshold` delegated votes. */
415
+ propose(args: {
416
+ targets: `0x${string}`[];
417
+ values: bigint[];
418
+ calldatas: `0x${string}`[];
419
+ description: string;
420
+ }): Promise<`0x${string}`>;
421
+ /** Queue a Succeeded proposal into the timelock. */
422
+ queue(args: {
423
+ targets: `0x${string}`[];
424
+ values: bigint[];
425
+ calldatas: `0x${string}`[];
426
+ descriptionHash: `0x${string}`;
427
+ }): Promise<`0x${string}`>;
428
+ /**
429
+ * Execute a Queued proposal. The Governor enforces
430
+ * `msg.value == sum(values)`; pass the sum as `value`.
431
+ */
432
+ execute(args: {
433
+ targets: `0x${string}`[];
434
+ values: bigint[];
435
+ calldatas: `0x${string}`[];
436
+ descriptionHash: `0x${string}`;
437
+ }): Promise<`0x${string}`>;
438
+ }
439
+ export {};