|
1 | 1 | import type { JsonRpcSigner } from 'ethers' |
2 | | -import { Wallet } from 'ethers' |
| 2 | +import { Wallet, JsonRpcProvider } from 'ethers' |
3 | 3 | import { createLedgerBroker } from './ledger' |
4 | 4 | import { createFineTuningBroker } from './fine-tuning/broker' |
5 | 5 | import { createInferenceBroker } from './inference/broker/broker' |
6 | 6 | import type { InferenceBroker } from './inference/broker/broker' |
7 | 7 | import type { LedgerBroker } from './ledger' |
8 | 8 | import type { FineTuningBroker } from './fine-tuning/broker' |
| 9 | +import { createReadOnlyInferenceBroker } from './inference/broker/read-only-broker' |
| 10 | +import type { ReadOnlyInferenceBroker } from './inference/broker/read-only-broker' |
| 11 | +import { |
| 12 | + TESTNET_CHAIN_ID, |
| 13 | + MAINNET_CHAIN_ID, |
| 14 | + HARDHAT_CHAIN_ID, |
| 15 | + CONTRACT_ADDRESSES, |
| 16 | + isDevMode, |
| 17 | +} from './constants' |
9 | 18 |
|
10 | | -// Network configurations |
11 | | -export const TESTNET_CHAIN_ID = 16602n |
12 | | -export const MAINNET_CHAIN_ID = 16661n |
13 | | -export const HARDHAT_CHAIN_ID = 31337n |
14 | | - |
15 | | -// Contract addresses for different networks |
16 | | -export const CONTRACT_ADDRESSES = { |
17 | | - testnet: { |
18 | | - ledger: '0xE70830508dAc0A97e6c087c75f402f9Be669E406', |
19 | | - inference: '0xa79F4c8311FF93C06b8CfB403690cc987c93F91E', |
20 | | - fineTuning: '0xC6C075D8039763C8f1EbE580be5ADdf2fd6941bA', |
21 | | - }, |
22 | | - testnetDev: { |
23 | | - ledger: '0x815B93ab4Ba4BDF530dbF1552649a3c534F8BbF7', |
24 | | - inference: '0x41bD7Ac5c19000A974D5c192bcd5FB67b56C85c5', |
25 | | - fineTuning: '0x4e4158DF35CfdC0ac63264D3E112F5B8E9a5c569', |
26 | | - }, |
27 | | - mainnet: { |
28 | | - ledger: '0x2dE54c845Cd948B72D2e32e39586fe89607074E3', |
29 | | - inference: '0x47340d900bdFec2BD393c626E12ea0656F938d84', |
30 | | - fineTuning: '0x4e3474095518883744ddf135b7E0A23301c7F9c0', |
31 | | - }, |
32 | | - hardhat: { |
33 | | - ledger: '0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0', |
34 | | - inference: '0x0165878A594ca255338adfa4d48449f69242Eb8F', |
35 | | - fineTuning: '0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0', |
36 | | - }, |
37 | | -} as const |
38 | | - |
39 | | -/** |
40 | | - * Check if dev mode is enabled |
41 | | - * Supports multiple ways to enable dev mode: |
42 | | - * - Node.js: ZG_DEV_MODE environment variable |
43 | | - * - Next.js: NEXT_PUBLIC_ZG_DEV_MODE environment variable (build-time) |
44 | | - * - Browser: localStorage 'ZG_DEV_MODE' = 'true' |
45 | | - * - Browser: URL parameter ?dev=true or ?ZG_DEV_MODE=true |
46 | | - */ |
47 | | -export function isDevMode(): boolean { |
48 | | - // Check Node.js / Next.js environment variables |
49 | | - if (typeof process !== 'undefined' && process.env) { |
50 | | - if ( |
51 | | - process.env.ZG_DEV_MODE === 'true' || |
52 | | - process.env.ZG_DEV_MODE === '1' |
53 | | - ) { |
54 | | - return true |
55 | | - } |
56 | | - if ( |
57 | | - process.env.NEXT_PUBLIC_ZG_DEV_MODE === 'true' || |
58 | | - process.env.NEXT_PUBLIC_ZG_DEV_MODE === '1' |
59 | | - ) { |
60 | | - return true |
61 | | - } |
62 | | - } |
63 | | - |
64 | | - // Check browser localStorage and URL parameters |
65 | | - if (typeof window !== 'undefined') { |
66 | | - // Check localStorage |
67 | | - try { |
68 | | - const localStorageValue = window.localStorage.getItem('ZG_DEV_MODE') |
69 | | - if (localStorageValue === 'true' || localStorageValue === '1') { |
70 | | - return true |
71 | | - } |
72 | | - } catch { |
73 | | - // localStorage not available |
74 | | - } |
75 | | - |
76 | | - // Check URL parameters |
77 | | - try { |
78 | | - const urlParams = new URLSearchParams(window.location.search) |
79 | | - const devParam = |
80 | | - urlParams.get('dev') || urlParams.get('ZG_DEV_MODE') |
81 | | - if (devParam === 'true' || devParam === '1') { |
82 | | - return true |
83 | | - } |
84 | | - } catch { |
85 | | - // URL parsing failed |
86 | | - } |
87 | | - } |
88 | | - |
89 | | - return false |
| 19 | +// Re-export constants for backward compatibility |
| 20 | +export { |
| 21 | + TESTNET_CHAIN_ID, |
| 22 | + MAINNET_CHAIN_ID, |
| 23 | + HARDHAT_CHAIN_ID, |
| 24 | + CONTRACT_ADDRESSES, |
| 25 | + isDevMode, |
90 | 26 | } |
91 | 27 |
|
92 | 28 | /** |
@@ -229,3 +165,103 @@ export async function createZGComputeNetworkBroker( |
229 | 165 | throw error |
230 | 166 | } |
231 | 167 | } |
| 168 | + |
| 169 | +/** |
| 170 | + * Read-only version of ZGComputeNetworkBroker that doesn't require wallet connection. |
| 171 | + * Provides access to public blockchain data without authentication. |
| 172 | + * |
| 173 | + * Use this broker to: |
| 174 | + * - Browse available AI providers before connecting wallet |
| 175 | + * - Fetch service information and pricing |
| 176 | + * - Get provider health metrics |
| 177 | + * |
| 178 | + * Limitations: |
| 179 | + * - Cannot perform authenticated operations (send requests, manage accounts, etc.) |
| 180 | + * - No ledger or fine-tuning services (require authentication) |
| 181 | + * - Read-only operations only |
| 182 | + */ |
| 183 | +export class ZGComputeNetworkReadOnlyBroker { |
| 184 | + public inference!: ReadOnlyInferenceBroker |
| 185 | + |
| 186 | + constructor(inferenceBroker: ReadOnlyInferenceBroker) { |
| 187 | + this.inference = inferenceBroker |
| 188 | + } |
| 189 | +} |
| 190 | + |
| 191 | +/** |
| 192 | + * createZGComputeNetworkReadOnlyBroker creates a read-only broker WITHOUT wallet connection |
| 193 | + * |
| 194 | + * This broker provides access to public blockchain data (e.g., list providers) without |
| 195 | + * requiring user authentication. Perfect for browsing services before connecting a wallet. |
| 196 | + * |
| 197 | + * @param rpcUrl - JSON-RPC endpoint URL (e.g., 'https://evmrpc-testnet.0g.ai') |
| 198 | + * @param chainId - Optional chain ID. If not provided, will be detected from RPC endpoint. |
| 199 | + * |
| 200 | + * @returns Read-only broker instance with inference.listService() and inference.listServiceWithDetail() |
| 201 | + * |
| 202 | + * @example |
| 203 | + * ```typescript |
| 204 | + * // Create read-only broker (no wallet needed!) |
| 205 | + * const broker = await createZGComputeNetworkReadOnlyBroker( |
| 206 | + * 'https://evmrpc-testnet.0g.ai' |
| 207 | + * ); |
| 208 | + * |
| 209 | + * // List all available providers (no authentication required) |
| 210 | + * const providers = await broker.inference.listService(); |
| 211 | + * console.log(`Found ${providers.length} providers`); |
| 212 | + * |
| 213 | + * // Get detailed provider info with health metrics |
| 214 | + * const providersWithHealth = await broker.inference.listServiceWithDetail(); |
| 215 | + * providersWithHealth.forEach(p => { |
| 216 | + * console.log(`${p.provider}: ${p.healthMetrics?.uptime}% uptime`); |
| 217 | + * }); |
| 218 | + * ``` |
| 219 | + * |
| 220 | + * @throws An error if the broker cannot be initialized. |
| 221 | + */ |
| 222 | +export async function createZGComputeNetworkReadOnlyBroker( |
| 223 | + rpcUrl: string, |
| 224 | + chainId?: number |
| 225 | +): Promise<ZGComputeNetworkReadOnlyBroker> { |
| 226 | + try { |
| 227 | + // Create provider to detect network if chainId not provided |
| 228 | + let detectedChainId = chainId |
| 229 | + if (!detectedChainId) { |
| 230 | + const provider = new JsonRpcProvider(rpcUrl) |
| 231 | + const network = await provider.getNetwork() |
| 232 | + detectedChainId = Number(network.chainId) |
| 233 | + } |
| 234 | + |
| 235 | + // Log detected network for debugging |
| 236 | + const chainIdBigInt = BigInt(detectedChainId) |
| 237 | + if (chainIdBigInt === MAINNET_CHAIN_ID) { |
| 238 | + console.log(`Detected mainnet (chain ID: ${detectedChainId})`) |
| 239 | + } else if (chainIdBigInt === TESTNET_CHAIN_ID) { |
| 240 | + if (isDevMode()) { |
| 241 | + console.log( |
| 242 | + `Detected testnet [DEV MODE] (chain ID: ${detectedChainId})` |
| 243 | + ) |
| 244 | + } else { |
| 245 | + console.log(`Detected testnet (chain ID: ${detectedChainId})`) |
| 246 | + } |
| 247 | + } else if (chainIdBigInt === HARDHAT_CHAIN_ID) { |
| 248 | + console.log(`Detected hardhat (chain ID: ${detectedChainId})`) |
| 249 | + } else { |
| 250 | + console.warn( |
| 251 | + `Unknown chain ID: ${detectedChainId}. Using testnet addresses as default.` |
| 252 | + ) |
| 253 | + } |
| 254 | + |
| 255 | + // Create read-only inference broker (no authentication!) |
| 256 | + // The broker will auto-detect contract addresses based on chainId |
| 257 | + const inferenceBroker = await createReadOnlyInferenceBroker( |
| 258 | + rpcUrl, |
| 259 | + detectedChainId |
| 260 | + ) |
| 261 | + |
| 262 | + const broker = new ZGComputeNetworkReadOnlyBroker(inferenceBroker) |
| 263 | + return broker |
| 264 | + } catch (error) { |
| 265 | + throw error |
| 266 | + } |
| 267 | +} |
0 commit comments