0g-orbit 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/LICENSE +21 -0
  3. package/README.md +151 -0
  4. package/dist/cli/cli.js +1 -1
  5. package/dist/storage.d.ts.map +1 -1
  6. package/dist/storage.js +23 -1
  7. package/dist/storage.js.map +1 -1
  8. package/package.json +28 -4
  9. package/examples/ai-chatbot/index.ts +0 -74
  10. package/examples/model-registry/index.ts +0 -137
  11. package/examples/quick-start/index.ts +0 -65
  12. package/packages/cli/package.json +0 -30
  13. package/packages/cli/src/cli.ts +0 -69
  14. package/packages/cli/src/commands/account.ts +0 -29
  15. package/packages/cli/src/commands/inference.ts +0 -103
  16. package/packages/cli/src/commands/init.ts +0 -71
  17. package/packages/cli/src/commands/storage.ts +0 -91
  18. package/packages/cli/src/utils.ts +0 -21
  19. package/packages/cli/tsconfig.json +0 -8
  20. package/packages/core/package.json +0 -35
  21. package/packages/core/src/errors.test.ts +0 -99
  22. package/packages/core/src/errors.ts +0 -79
  23. package/packages/core/src/index.ts +0 -37
  24. package/packages/core/src/inference.ts +0 -256
  25. package/packages/core/src/networks.test.ts +0 -62
  26. package/packages/core/src/networks.ts +0 -62
  27. package/packages/core/src/orbit.test.ts +0 -153
  28. package/packages/core/src/orbit.ts +0 -159
  29. package/packages/core/src/retry.test.ts +0 -99
  30. package/packages/core/src/retry.ts +0 -99
  31. package/packages/core/src/storage.test.ts +0 -199
  32. package/packages/core/src/storage.ts +0 -158
  33. package/packages/core/src/types.ts +0 -85
  34. package/packages/core/tsconfig.json +0 -8
  35. package/packages/core/vitest.config.ts +0 -7
  36. package/src/cli/cli.ts +0 -95
  37. package/src/cli/commands/account.ts +0 -29
  38. package/src/cli/commands/fine-tuning.ts +0 -169
  39. package/src/cli/commands/inference.ts +0 -103
  40. package/src/cli/commands/init.ts +0 -71
  41. package/src/cli/commands/storage.ts +0 -91
  42. package/src/cli/utils.ts +0 -21
  43. package/src/errors.test.ts +0 -99
  44. package/src/errors.ts +0 -90
  45. package/src/fine-tuning.test.ts +0 -299
  46. package/src/fine-tuning.ts +0 -330
  47. package/src/index.ts +0 -45
  48. package/src/inference.ts +0 -256
  49. package/src/networks.test.ts +0 -62
  50. package/src/networks.ts +0 -62
  51. package/src/orbit.test.ts +0 -153
  52. package/src/orbit.ts +0 -204
  53. package/src/retry.test.ts +0 -99
  54. package/src/retry.ts +0 -99
  55. package/src/storage.test.ts +0 -199
  56. package/src/storage.ts +0 -158
  57. package/src/types.ts +0 -157
  58. package/tsconfig.json +0 -20
  59. package/vitest.config.ts +0 -7
@@ -1,330 +0,0 @@
1
- import { Wallet } from 'ethers'
2
- import {
3
- createZGComputeNetworkBroker,
4
- type ZGComputeNetworkBroker,
5
- } from '@0glabs/0g-serving-broker'
6
- import type { NetworkConfig } from './networks.js'
7
- import type {
8
- DatasetUploadResult,
9
- CreateTaskOptions,
10
- FineTuneTask,
11
- FineTuneModel,
12
- FineTuneProvider,
13
- FineTuneStatus,
14
- } from './types.js'
15
- import { FineTuningError } from './errors.js'
16
- import { withRetry } from './retry.js'
17
- import { StorageClient } from './storage.js'
18
-
19
- const DEFAULT_TRAINING_PARAMS = {
20
- nEpochs: 3,
21
- batchSize: 4,
22
- learningRate: 5e-5,
23
- loraRank: 8,
24
- loraAlpha: 16,
25
- }
26
-
27
- export class FineTuningClient {
28
- private broker: ZGComputeNetworkBroker | null = null
29
- private initialized = false
30
- private wallet: Wallet
31
- private network: NetworkConfig
32
- private storageClient: StorageClient
33
-
34
- constructor(network: NetworkConfig, wallet: Wallet, storageClient: StorageClient) {
35
- this.network = network
36
- this.wallet = wallet
37
- this.storageClient = storageClient
38
- }
39
-
40
- private async ensureBroker(): Promise<ZGComputeNetworkBroker> {
41
- if (this.broker && this.initialized) return this.broker
42
-
43
- try {
44
- this.broker = await createZGComputeNetworkBroker(
45
- this.wallet as any,
46
- this.network.ledgerContractAddress,
47
- this.network.inferenceContractAddress,
48
- this.network.fineTuningContractAddress
49
- )
50
- this.initialized = true
51
- return this.broker
52
- } catch (err: unknown) {
53
- const msg = err instanceof Error ? err.message : String(err)
54
- throw new FineTuningError(
55
- `Failed to initialize fine-tuning broker: ${msg}`,
56
- 'Check your network connection and wallet private key. The compute contracts may be temporarily unavailable.'
57
- )
58
- }
59
- }
60
-
61
- // --- Dataset ---
62
-
63
- /**
64
- * Upload a dataset to 0G Storage for fine-tuning.
65
- * Uses the existing StorageClient for upload, returning the root hash
66
- * needed to create a fine-tuning task.
67
- */
68
- async uploadDataset(filePath: string): Promise<DatasetUploadResult> {
69
- return withRetry(
70
- async () => {
71
- try {
72
- const result = await this.storageClient.store(filePath)
73
- return {
74
- root: result.root,
75
- txHash: result.txHash,
76
- }
77
- } catch (err: unknown) {
78
- const msg = err instanceof Error ? err.message : String(err)
79
- throw new FineTuningError(
80
- `Failed to upload dataset: ${msg}`,
81
- 'Check that the dataset file exists, is valid JSONL, and you have sufficient OG balance.'
82
- )
83
- }
84
- },
85
- { maxAttempts: 3 }
86
- )
87
- }
88
-
89
- // --- Tasks ---
90
-
91
- /**
92
- * Create a fine-tuning task. Requires a dataset already uploaded to 0G Storage.
93
- */
94
- async createTask(options: CreateTaskOptions): Promise<FineTuneTask> {
95
- const broker = await this.ensureBroker()
96
-
97
- if (!broker.fineTuning) {
98
- throw new FineTuningError(
99
- 'Fine-tuning broker not available.',
100
- 'Fine-tuning requires a Wallet signer (not JsonRpcSigner).'
101
- )
102
- }
103
-
104
- const params = { ...DEFAULT_TRAINING_PARAMS, ...options.trainingParams }
105
-
106
- return withRetry(
107
- async () => {
108
- try {
109
- // Acknowledge the provider signer (required before task creation)
110
- await broker.fineTuning!.acknowledgeProviderSigner(
111
- options.providerAddress
112
- )
113
-
114
- // Write training params as JSON string (broker expects a file path,
115
- // but the underlying createTask reads the file content — we'll pass
116
- // the JSON string directly via a temp file approach)
117
- const { writeFileSync, unlinkSync } = await import('node:fs')
118
- const { tmpdir } = await import('node:os')
119
- const { join } = await import('node:path')
120
- const { randomBytes } = await import('node:crypto')
121
-
122
- const tempPath = join(tmpdir(), `orbit-params-${randomBytes(8).toString('hex')}.json`)
123
- writeFileSync(tempPath, JSON.stringify(params))
124
-
125
- let taskId: string
126
- try {
127
- taskId = await broker.fineTuning!.createTask(
128
- options.providerAddress,
129
- options.model,
130
- options.dataset,
131
- tempPath
132
- )
133
- } finally {
134
- try { unlinkSync(tempPath) } catch { /* ignore cleanup errors */ }
135
- }
136
-
137
- return {
138
- id: taskId,
139
- model: options.model,
140
- dataset: options.dataset,
141
- provider: options.providerAddress,
142
- status: 'init' as FineTuneStatus,
143
- }
144
- } catch (err: unknown) {
145
- const msg = err instanceof Error ? err.message : String(err)
146
- if (msg.includes('User opted not to continue')) {
147
- throw new FineTuningError(msg, 'The provider has pending tasks in queue. Try again later or use a different provider.')
148
- }
149
- throw new FineTuningError(
150
- `Failed to create task: ${msg}`,
151
- 'Verify the model name, dataset hash, and provider address. Run orbit.listModels() to see available options.'
152
- )
153
- }
154
- },
155
- { maxAttempts: 2 }
156
- )
157
- }
158
-
159
- /**
160
- * Get the status of a fine-tuning task.
161
- */
162
- async getTask(providerAddress: string, taskId: string): Promise<FineTuneTask> {
163
- const broker = await this.ensureBroker()
164
-
165
- if (!broker.fineTuning) {
166
- throw new FineTuningError('Fine-tuning broker not available.')
167
- }
168
-
169
- try {
170
- const task = await broker.fineTuning.getTask(providerAddress, taskId)
171
- return this.mapTask(task, providerAddress)
172
- } catch (err: unknown) {
173
- const msg = err instanceof Error ? err.message : String(err)
174
- if (msg.includes('No task found') || msg.includes('not found')) {
175
- throw new FineTuningError(
176
- `Task "${taskId}" not found for provider ${providerAddress}.`,
177
- 'Check the task ID and provider address. Run orbit.listTasks() to see your tasks.'
178
- )
179
- }
180
- throw new FineTuningError(`Failed to get task: ${msg}`)
181
- }
182
- }
183
-
184
- /**
185
- * Get the training log for a fine-tuning task.
186
- */
187
- async getTaskLog(providerAddress: string, taskId: string): Promise<string> {
188
- const broker = await this.ensureBroker()
189
-
190
- if (!broker.fineTuning) {
191
- throw new FineTuningError('Fine-tuning broker not available.')
192
- }
193
-
194
- try {
195
- return await broker.fineTuning.getLog(providerAddress, taskId)
196
- } catch (err: unknown) {
197
- const msg = err instanceof Error ? err.message : String(err)
198
- throw new FineTuningError(`Failed to get task log: ${msg}`)
199
- }
200
- }
201
-
202
- /**
203
- * List all fine-tuning tasks for a given provider.
204
- */
205
- async listTasks(providerAddress: string): Promise<FineTuneTask[]> {
206
- const broker = await this.ensureBroker()
207
-
208
- if (!broker.fineTuning) {
209
- throw new FineTuningError('Fine-tuning broker not available.')
210
- }
211
-
212
- try {
213
- const tasks = await broker.fineTuning.listTask(providerAddress)
214
- return tasks.map((t) => this.mapTask(t, providerAddress))
215
- } catch (err: unknown) {
216
- const msg = err instanceof Error ? err.message : String(err)
217
- throw new FineTuningError(`Failed to list tasks: ${msg}`)
218
- }
219
- }
220
-
221
- // --- Models ---
222
-
223
- /**
224
- * Download a fine-tuned model. Combines acknowledge + download.
225
- * The task must be in 'delivered' status.
226
- */
227
- async downloadModel(
228
- providerAddress: string,
229
- taskId: string,
230
- outputPath: string
231
- ): Promise<void> {
232
- const broker = await this.ensureBroker()
233
-
234
- if (!broker.fineTuning) {
235
- throw new FineTuningError('Fine-tuning broker not available.')
236
- }
237
-
238
- return withRetry(
239
- async () => {
240
- try {
241
- await broker.fineTuning!.acknowledgeModel(
242
- providerAddress,
243
- taskId,
244
- outputPath
245
- )
246
- } catch (err: unknown) {
247
- const msg = err instanceof Error ? err.message : String(err)
248
- if (msg.includes('No deliverable found')) {
249
- throw new FineTuningError(
250
- `Model not ready for task "${taskId}". The task may still be training.`,
251
- 'Check task status with orbit.getFineTuneTask(). The task must be in "delivered" status.'
252
- )
253
- }
254
- throw new FineTuningError(
255
- `Failed to download model: ${msg}`,
256
- 'Ensure the task is in "delivered" status and the output path is writable.'
257
- )
258
- }
259
- },
260
- { maxAttempts: 2 }
261
- )
262
- }
263
-
264
- /**
265
- * List available base models for fine-tuning.
266
- */
267
- async listModels(): Promise<FineTuneModel[]> {
268
- const broker = await this.ensureBroker()
269
-
270
- if (!broker.fineTuning) {
271
- throw new FineTuningError('Fine-tuning broker not available.')
272
- }
273
-
274
- try {
275
- const [standardModels, customizedModels] = await broker.fineTuning.listModel()
276
-
277
- const models: FineTuneModel[] = []
278
-
279
- for (const [name, config] of standardModels) {
280
- models.push({ name, config })
281
- }
282
-
283
- for (const [name, config] of customizedModels) {
284
- models.push({ name, config })
285
- }
286
-
287
- return models
288
- } catch (err: unknown) {
289
- const msg = err instanceof Error ? err.message : String(err)
290
- throw new FineTuningError(`Failed to list models: ${msg}`)
291
- }
292
- }
293
-
294
- /**
295
- * List fine-tuning service providers.
296
- */
297
- async listProviders(): Promise<FineTuneProvider[]> {
298
- const broker = await this.ensureBroker()
299
-
300
- if (!broker.fineTuning) {
301
- throw new FineTuningError('Fine-tuning broker not available.')
302
- }
303
-
304
- try {
305
- const services = await broker.fineTuning.listService()
306
- return services.map((s: any) => ({
307
- address: s.provider ?? '',
308
- url: s.url ?? '',
309
- models: s.models ?? [],
310
- }))
311
- } catch (err: unknown) {
312
- const msg = err instanceof Error ? err.message : String(err)
313
- throw new FineTuningError(`Failed to list providers: ${msg}`)
314
- }
315
- }
316
-
317
- // --- Helpers ---
318
-
319
- private mapTask(task: any, providerAddress: string): FineTuneTask {
320
- return {
321
- id: task.id ?? '',
322
- model: task.preTrainedModelHash ?? '',
323
- dataset: task.datasetHash ?? '',
324
- provider: providerAddress,
325
- status: (task.progress?.toLowerCase() ?? 'init') as FineTuneStatus,
326
- createdAt: task.createdAt,
327
- updatedAt: task.updatedAt,
328
- }
329
- }
330
- }
package/src/index.ts DELETED
@@ -1,45 +0,0 @@
1
- // Main entry point
2
- export { Orbit } from './orbit.js'
3
-
4
- // Clients (for direct access)
5
- export { StorageClient } from './storage.js'
6
- export { InferenceClient } from './inference.js'
7
- export { FineTuningClient } from './fine-tuning.js'
8
-
9
- // Types
10
- export type {
11
- OrbitConfig,
12
- StoreResult,
13
- StoreOptions,
14
- RetrieveOptions,
15
- InferResult,
16
- InferOptions,
17
- ServiceInfo,
18
- AccountStatus,
19
- DatasetUploadResult,
20
- CreateTaskOptions,
21
- FineTuneTask,
22
- FineTuneStatus,
23
- FineTuneModel,
24
- FineTuneProvider,
25
- } from './types.js'
26
-
27
- // Network config
28
- export type { NetworkName, NetworkConfig } from './networks.js'
29
- export { NETWORKS, getNetwork } from './networks.js'
30
-
31
- // Retry utilities
32
- export { withRetry, isTransientError } from './retry.js'
33
- export type { RetryOptions } from './retry.js'
34
-
35
- // Errors
36
- export {
37
- OrbitError,
38
- ConnectionError,
39
- StorageError,
40
- InferenceError,
41
- InsufficientBalanceError,
42
- ProviderNotFoundError,
43
- TimeoutError,
44
- FineTuningError,
45
- } from './errors.js'
package/src/inference.ts DELETED
@@ -1,256 +0,0 @@
1
- import { Wallet } from 'ethers'
2
- import {
3
- createZGComputeNetworkBroker,
4
- type ZGComputeNetworkBroker,
5
- } from '@0glabs/0g-serving-broker'
6
- import type { NetworkConfig } from './networks.js'
7
- import type { InferResult, InferOptions, ServiceInfo } from './types.js'
8
- import { InferenceError, ProviderNotFoundError, TimeoutError } from './errors.js'
9
- import { withRetry } from './retry.js'
10
-
11
- const DEFAULT_LEDGER_DEPOSIT = 0.1 // 0.1 OG initial deposit
12
- const AUTO_FUND_INTERVAL = 30_000 // 30 seconds
13
- const DEFAULT_TIMEOUT = 30_000 // 30 seconds
14
-
15
- interface ChatCompletionResponse {
16
- id?: string
17
- model?: string
18
- choices?: Array<{ message?: { content?: string } }>
19
- usage?: {
20
- prompt_tokens?: number
21
- completion_tokens?: number
22
- total_tokens?: number
23
- }
24
- }
25
-
26
- export class InferenceClient {
27
- private broker: ZGComputeNetworkBroker | null = null
28
- private wallet: Wallet
29
- private network: NetworkConfig
30
- private initialized = false
31
-
32
- constructor(network: NetworkConfig, wallet: Wallet) {
33
- this.network = network
34
- this.wallet = wallet
35
- }
36
-
37
- private async ensureBroker(): Promise<ZGComputeNetworkBroker> {
38
- if (this.broker && this.initialized) return this.broker
39
-
40
- try {
41
- // Cast wallet to avoid ESM/CJS ethers type mismatch
42
- this.broker = await createZGComputeNetworkBroker(
43
- this.wallet as any,
44
- this.network.ledgerContractAddress,
45
- this.network.inferenceContractAddress,
46
- this.network.fineTuningContractAddress
47
- )
48
- this.initialized = true
49
- return this.broker
50
- } catch (err: unknown) {
51
- const msg = err instanceof Error ? err.message : String(err)
52
- throw new InferenceError(
53
- `Failed to initialize compute broker: ${msg}`,
54
- 'Check your network connection and wallet private key. The compute contracts may be temporarily unavailable.'
55
- )
56
- }
57
- }
58
-
59
- async listServices(): Promise<ServiceInfo[]> {
60
- const broker = await this.ensureBroker()
61
- return withRetry(
62
- async () => {
63
- try {
64
- const services = await broker.inference.listService()
65
- return services.map((s: any) => ({
66
- provider: s.provider ?? s.address ?? '',
67
- model: s.model ?? '',
68
- url: s.url ?? '',
69
- inputPrice: BigInt(s.inputPrice ?? 0),
70
- outputPrice: BigInt(s.outputPrice ?? 0),
71
- verifiable: Boolean(s.verifiability ?? s.verifiable),
72
- }))
73
- } catch (err: unknown) {
74
- const msg = err instanceof Error ? err.message : String(err)
75
- throw new InferenceError(
76
- `Failed to list services: ${msg}`,
77
- 'The inference contract may be temporarily unavailable. Check your network connection.'
78
- )
79
- }
80
- },
81
- { maxAttempts: 3 }
82
- )
83
- }
84
-
85
- async infer(
86
- model: string,
87
- options: InferOptions
88
- ): Promise<InferResult> {
89
- const broker = await this.ensureBroker()
90
-
91
- // Find a provider for the requested model
92
- const providerAddress = options.provider ?? await this.findProvider(model)
93
- const timeout = options.timeout ?? DEFAULT_TIMEOUT
94
-
95
- try {
96
- // Ensure ledger exists and has funds
97
- await this.ensureLedgerFunded(broker)
98
-
99
- // Start auto-funding for this provider
100
- await broker.inference.startAutoFunding(providerAddress, {
101
- interval: AUTO_FUND_INTERVAL,
102
- })
103
-
104
- // Get service metadata (endpoint + model name)
105
- const { endpoint, model: providerModel } =
106
- await broker.inference.getServiceMetadata(providerAddress)
107
-
108
- // Build the request content for billing calculation
109
- const content = options.messages.map((m) => m.content).join('\n')
110
-
111
- // Get authenticated headers
112
- const headers = await broker.inference.getRequestHeaders(
113
- providerAddress,
114
- content
115
- )
116
-
117
- // Make the OpenAI-compatible request with timeout + retry
118
- const data = await withRetry(
119
- () => this.fetchCompletion(endpoint, providerModel, options, headers, timeout),
120
- {
121
- maxAttempts: 2,
122
- baseDelay: 2000,
123
- isRetryable: (err) => {
124
- // Don't retry timeouts (user already waited long enough)
125
- if (err instanceof TimeoutError) return false
126
- // Retry transient provider errors
127
- if (err instanceof InferenceError) {
128
- const msg = err.message
129
- return msg.includes('502') || msg.includes('503') || msg.includes('504')
130
- }
131
- return false
132
- },
133
- }
134
- )
135
-
136
- // Extract chatID for TEE verification
137
- const chatID = data._chatID
138
-
139
- // Process response (caches fees + verifies TEE signature)
140
- let verified: boolean | null = null
141
- try {
142
- verified = await broker.inference.processResponse(
143
- providerAddress,
144
- chatID,
145
- data.usage ? JSON.stringify(data.usage) : undefined
146
- )
147
- } catch {
148
- // Verification failure is non-fatal
149
- verified = null
150
- }
151
-
152
- // Stop auto-funding
153
- broker.inference.stopAutoFunding(providerAddress)
154
-
155
- return {
156
- content: data.choices?.[0]?.message?.content ?? '',
157
- model: data.model ?? providerModel,
158
- usage: data.usage
159
- ? {
160
- promptTokens: data.usage.prompt_tokens ?? 0,
161
- completionTokens: data.usage.completion_tokens ?? 0,
162
- totalTokens: data.usage.total_tokens ?? 0,
163
- }
164
- : undefined,
165
- verified,
166
- }
167
- } catch (err) {
168
- // Clean up auto-funding on error
169
- broker.inference.stopAutoFunding(providerAddress)
170
-
171
- if (err instanceof InferenceError || err instanceof TimeoutError || err instanceof ProviderNotFoundError) throw err
172
- const msg = err instanceof Error ? err.message : String(err)
173
- throw new InferenceError(`Inference failed: ${msg}`)
174
- }
175
- }
176
-
177
- private async fetchCompletion(
178
- endpoint: string,
179
- providerModel: string,
180
- options: InferOptions,
181
- headers: Record<string, string> | object,
182
- timeout: number
183
- ): Promise<ChatCompletionResponse & { _chatID?: string }> {
184
- const controller = new AbortController()
185
- const timer = setTimeout(() => controller.abort(), timeout)
186
-
187
- let response: Response
188
- try {
189
- response = await fetch(`${endpoint}/chat/completions`, {
190
- method: 'POST',
191
- headers: {
192
- 'Content-Type': 'application/json',
193
- ...headers,
194
- },
195
- body: JSON.stringify({
196
- model: providerModel,
197
- messages: options.messages,
198
- temperature: options.temperature,
199
- max_tokens: options.maxTokens,
200
- }),
201
- signal: controller.signal,
202
- })
203
- } catch (err) {
204
- if (err instanceof Error && err.name === 'AbortError') {
205
- throw new TimeoutError(
206
- `Inference request timed out after ${timeout / 1000}s.`
207
- )
208
- }
209
- throw err
210
- } finally {
211
- clearTimeout(timer)
212
- }
213
-
214
- if (!response.ok) {
215
- const body = await response.text().catch(() => '')
216
- throw new InferenceError(
217
- `Provider returned ${response.status}: ${body}`,
218
- response.status === 429
219
- ? 'Provider is rate-limited. Wait a moment and retry, or try a different provider.'
220
- : response.status >= 500
221
- ? 'The provider is experiencing issues. Try a different provider with the provider option.'
222
- : 'Check the model name and request parameters.'
223
- )
224
- }
225
-
226
- const data = (await response.json()) as ChatCompletionResponse
227
- const chatID = response.headers.get('ZG-Res-Key') ?? data.id ?? undefined
228
-
229
- return { ...data, _chatID: chatID }
230
- }
231
-
232
- private async findProvider(model: string): Promise<string> {
233
- const services = await this.listServices()
234
- const match = services.find(
235
- (s) => s.model.toLowerCase() === model.toLowerCase()
236
- )
237
- if (!match) {
238
- const available = [...new Set(services.map((s) => s.model))].join(', ') || 'none'
239
- throw new ProviderNotFoundError(
240
- `No provider found for model "${model}". Available models: ${available}`
241
- )
242
- }
243
- return match.provider
244
- }
245
-
246
- private async ensureLedgerFunded(
247
- broker: ZGComputeNetworkBroker
248
- ): Promise<void> {
249
- try {
250
- await broker.ledger.getLedger()
251
- } catch {
252
- // Ledger doesn't exist yet — create it with initial deposit
253
- await broker.ledger.addLedger(DEFAULT_LEDGER_DEPOSIT)
254
- }
255
- }
256
- }
@@ -1,62 +0,0 @@
1
- import { describe, it, expect } from 'vitest'
2
- import { getNetwork, NETWORKS } from './networks.js'
3
-
4
- describe('NETWORKS', () => {
5
- it('testnet has correct chain ID', () => {
6
- expect(NETWORKS.testnet.chainId).toBe(16602n)
7
- })
8
-
9
- it('mainnet has correct chain ID', () => {
10
- expect(NETWORKS.mainnet.chainId).toBe(16661n)
11
- })
12
-
13
- it('testnet has multiple fallback RPC URLs', () => {
14
- expect(NETWORKS.testnet.rpcUrls.length).toBeGreaterThanOrEqual(2)
15
- expect(NETWORKS.testnet.rpcUrls[0]).toBe(NETWORKS.testnet.rpcUrl)
16
- })
17
-
18
- it('mainnet has multiple fallback RPC URLs', () => {
19
- expect(NETWORKS.mainnet.rpcUrls.length).toBeGreaterThanOrEqual(2)
20
- expect(NETWORKS.mainnet.rpcUrls[0]).toBe(NETWORKS.mainnet.rpcUrl)
21
- })
22
-
23
- it('all contract addresses are non-empty hex strings', () => {
24
- for (const net of Object.values(NETWORKS)) {
25
- expect(net.flowContractAddress).toMatch(/^0x[0-9a-fA-F]+$/)
26
- expect(net.ledgerContractAddress).toMatch(/^0x[0-9a-fA-F]+$/)
27
- expect(net.inferenceContractAddress).toMatch(/^0x[0-9a-fA-F]+$/)
28
- expect(net.fineTuningContractAddress).toMatch(/^0x[0-9a-fA-F]+$/)
29
- }
30
- })
31
- })
32
-
33
- describe('getNetwork', () => {
34
- it('resolves by name', () => {
35
- const net = getNetwork('testnet')
36
- expect(net.name).toBe('testnet')
37
- expect(net.chainId).toBe(16602n)
38
- })
39
-
40
- it('resolves by chain ID', () => {
41
- const net = getNetwork(16661n)
42
- expect(net.name).toBe('mainnet')
43
- })
44
-
45
- it('throws on unknown name', () => {
46
- expect(() => getNetwork('devnet' as any)).toThrow('Unknown network')
47
- })
48
-
49
- it('throws on unknown chain ID', () => {
50
- expect(() => getNetwork(99999n)).toThrow('Unknown chain ID')
51
- })
52
-
53
- it('returns a copy — mutations do not affect originals', () => {
54
- const net = getNetwork('testnet')
55
- net.rpcUrl = 'https://mutated.example.com'
56
- net.rpcUrls.push('https://extra.example.com')
57
-
58
- const fresh = getNetwork('testnet')
59
- expect(fresh.rpcUrl).not.toBe('https://mutated.example.com')
60
- expect(fresh.rpcUrls).not.toContain('https://extra.example.com')
61
- })
62
- })