@no-witness-labs/midday-sdk 0.2.0 → 0.2.2

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 (66) hide show
  1. package/README.md +145 -78
  2. package/dist/Client.d.ts +542 -70
  3. package/dist/Client.d.ts.map +1 -1
  4. package/dist/Client.js +608 -143
  5. package/dist/Client.js.map +1 -1
  6. package/dist/Config.d.ts +83 -1
  7. package/dist/Config.d.ts.map +1 -1
  8. package/dist/Config.js +72 -15
  9. package/dist/Config.js.map +1 -1
  10. package/dist/Providers.d.ts +99 -9
  11. package/dist/Providers.d.ts.map +1 -1
  12. package/dist/Providers.js +142 -39
  13. package/dist/Providers.js.map +1 -1
  14. package/dist/Wallet.d.ts +88 -1
  15. package/dist/Wallet.d.ts.map +1 -1
  16. package/dist/Wallet.js +162 -51
  17. package/dist/Wallet.js.map +1 -1
  18. package/dist/index.d.ts +63 -7
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +68 -4
  21. package/dist/index.js.map +1 -1
  22. package/dist/providers/HttpZkConfigProvider.d.ts +229 -0
  23. package/dist/providers/HttpZkConfigProvider.d.ts.map +1 -0
  24. package/dist/providers/HttpZkConfigProvider.js +275 -0
  25. package/dist/providers/HttpZkConfigProvider.js.map +1 -0
  26. package/dist/providers/IndexedDBPrivateStateProvider.d.ts +270 -0
  27. package/dist/providers/IndexedDBPrivateStateProvider.d.ts.map +1 -0
  28. package/dist/providers/IndexedDBPrivateStateProvider.js +513 -0
  29. package/dist/providers/IndexedDBPrivateStateProvider.js.map +1 -0
  30. package/dist/providers/errors.d.ts +50 -0
  31. package/dist/providers/errors.d.ts.map +1 -0
  32. package/dist/providers/errors.js +32 -0
  33. package/dist/providers/errors.js.map +1 -0
  34. package/dist/sdk/Type.d.ts +91 -0
  35. package/dist/sdk/Type.d.ts.map +1 -0
  36. package/dist/sdk/Type.js +8 -0
  37. package/dist/sdk/Type.js.map +1 -0
  38. package/dist/utils/address.d.ts +56 -0
  39. package/dist/utils/address.d.ts.map +1 -0
  40. package/dist/utils/address.js +135 -0
  41. package/dist/utils/address.js.map +1 -0
  42. package/dist/utils/coin.d.ts +55 -0
  43. package/dist/utils/coin.d.ts.map +1 -0
  44. package/dist/utils/coin.js +84 -0
  45. package/dist/utils/coin.js.map +1 -0
  46. package/dist/utils/effect-runtime.d.ts +66 -0
  47. package/dist/utils/effect-runtime.d.ts.map +1 -0
  48. package/dist/utils/effect-runtime.js +147 -0
  49. package/dist/utils/effect-runtime.js.map +1 -0
  50. package/dist/utils/hex.d.ts +62 -0
  51. package/dist/utils/hex.d.ts.map +1 -0
  52. package/dist/utils/hex.js +93 -0
  53. package/dist/utils/hex.js.map +1 -0
  54. package/dist/wallet/connector.d.ts +191 -0
  55. package/dist/wallet/connector.d.ts.map +1 -0
  56. package/dist/wallet/connector.js +183 -0
  57. package/dist/wallet/connector.js.map +1 -0
  58. package/dist/wallet/errors.d.ts +22 -0
  59. package/dist/wallet/errors.d.ts.map +1 -0
  60. package/dist/wallet/errors.js +16 -0
  61. package/dist/wallet/errors.js.map +1 -0
  62. package/dist/wallet/provider.d.ts +102 -0
  63. package/dist/wallet/provider.d.ts.map +1 -0
  64. package/dist/wallet/provider.js +139 -0
  65. package/dist/wallet/provider.js.map +1 -0
  66. package/package.json +10 -5
package/dist/Client.js CHANGED
@@ -1,160 +1,276 @@
1
1
  /**
2
2
  * High-level client for interacting with Midnight Network contracts.
3
3
  *
4
- * Provides a simple API for deploying, joining, and calling contracts.
4
+ * ## API Design
5
+ *
6
+ * This module uses a **module-function pattern**:
7
+ *
8
+ * - **Stateless**: Functions operate on Client/Contract data
9
+ * - **Module functions**: `Client.contractFrom(client, options)`, `Contract.deploy(builder)`
10
+ * - **Data-oriented**: Client/Contract are plain data, not instances with methods
11
+ *
12
+ * ### Usage Patterns
13
+ *
14
+ * ```typescript
15
+ * // Promise user
16
+ * const client = await Client.create(config);
17
+ * const builder = await Client.contractFrom(client, { module });
18
+ * const contract = await ContractBuilder.deploy(builder);
19
+ * const result = await Contract.call(contract, 'increment');
20
+ *
21
+ * // Effect user
22
+ * const client = yield* Client.effect.create(config);
23
+ * const builder = yield* Client.effect.contractFrom(client, { module });
24
+ * const contract = yield* ContractBuilder.effect.deploy(builder);
25
+ * const result = yield* Contract.effect.call(contract, 'increment');
26
+ * ```
5
27
  *
6
28
  * @since 0.1.0
7
29
  * @module
8
30
  */
9
- import * as path from 'path';
10
- import * as fs from 'fs';
11
- import { fileURLToPath } from 'url';
31
+ import { Context, Data, Effect, Layer } from 'effect';
12
32
  import { deployContract, findDeployedContract } from '@midnight-ntwrk/midnight-js-contracts';
13
- import pino from 'pino';
14
- import { build as buildPretty } from 'pino-pretty';
15
33
  import * as Config from './Config.js';
16
34
  import * as Wallet from './Wallet.js';
17
35
  import * as Providers from './Providers.js';
36
+ import { createWalletProviders } from './wallet/provider.js';
37
+ import { runEffectPromise } from './utils/effect-runtime.js';
18
38
  // =============================================================================
19
- // Path Resolution
39
+ // Errors
20
40
  // =============================================================================
21
- function findProjectRoot(startDir = process.cwd()) {
22
- let current = startDir;
23
- while (current !== path.dirname(current)) {
24
- if (fs.existsSync(path.join(current, 'package.json'))) {
25
- return current;
26
- }
27
- current = path.dirname(current);
28
- }
29
- throw new Error('Could not find project root (no package.json found)');
41
+ /**
42
+ * Error during client initialization or operation.
43
+ *
44
+ * @since 0.3.0
45
+ * @category errors
46
+ */
47
+ export class ClientError extends Data.TaggedError('ClientError') {
30
48
  }
31
- function resolvePath(basePath, options) {
32
- if (path.isAbsolute(basePath)) {
33
- return basePath;
34
- }
35
- const from = options?.from ?? 'cwd';
36
- if (from === 'cwd') {
37
- return path.resolve(process.cwd(), basePath);
38
- }
39
- if (from === 'project') {
40
- const projectRoot = findProjectRoot();
41
- return path.resolve(projectRoot, basePath);
42
- }
43
- // Assume it's an import.meta.url
44
- const callerDir = path.dirname(fileURLToPath(from));
45
- return path.resolve(callerDir, basePath);
49
+ /**
50
+ * Error during contract deployment or calls.
51
+ *
52
+ * @since 0.3.0
53
+ * @category errors
54
+ */
55
+ export class ContractError extends Data.TaggedError('ContractError') {
46
56
  }
47
57
  // =============================================================================
48
- // Logger
58
+ // Logger Factory
49
59
  // =============================================================================
50
60
  function createLogger(enabled) {
51
61
  if (!enabled) {
52
- return pino.default({ level: 'silent' });
62
+ return {
63
+ info: () => { },
64
+ warn: () => { },
65
+ error: () => { },
66
+ debug: () => { },
67
+ };
53
68
  }
54
- const pretty = buildPretty({ colorize: true, sync: true });
55
- return pino.default({ level: 'info' }, pretty);
69
+ return {
70
+ info: (message) => console.log(`[INFO] ${message}`),
71
+ warn: (message) => console.warn(`[WARN] ${message}`),
72
+ error: (message) => console.error(`[ERROR] ${message}`),
73
+ debug: (message) => console.debug(`[DEBUG] ${message}`),
74
+ };
56
75
  }
57
76
  // =============================================================================
58
- // Client Implementation
77
+ // Client - Internal Effect Implementations
59
78
  // =============================================================================
60
- /**
61
- * Create a Midnight client for interacting with contracts
62
- *
63
- * @example
64
- * ```typescript
65
- * import * as Midday from '@no-witness-labs/midday-sdk';
66
- *
67
- * // Simple - local network with dev wallet
68
- * const client = await Midday.Client.create();
69
- *
70
- * // Custom seed
71
- * const client = await Midday.Client.create({
72
- * seed: 'your-64-char-hex-seed'
73
- * });
74
- *
75
- * // Custom network endpoints via env vars or config
76
- * const client = await Midday.Client.create({
77
- * networkConfig: {
78
- * networkId: 'testnet',
79
- * indexer: 'https://indexer.testnet.midnight.network/graphql',
80
- * indexerWS: 'wss://indexer.testnet.midnight.network/graphql/ws',
81
- * node: 'wss://node.testnet.midnight.network',
82
- * proofServer: 'https://proof.testnet.midnight.network',
83
- * }
84
- * });
85
- *
86
- * // Load and deploy a contract
87
- * const counter = await (await client.contractFrom('build/simple-counter')).deploy();
88
- *
89
- * // Call actions
90
- * await counter.call('increment');
91
- *
92
- * // Read state
93
- * const state = await counter.ledgerState();
94
- * console.log(state.counter);
95
- *
96
- * // Join existing contract
97
- * const existing = await (await client.contractFrom('build/simple-counter')).join(address);
98
- * ```
99
- */
100
- export async function create(config = {}) {
101
- const { network = 'local', networkConfig: customNetworkConfig, seed, storage, logging = true } = config;
102
- const logger = createLogger(logging);
103
- // Resolve network configuration
104
- const networkConfig = customNetworkConfig ?? Config.getNetworkConfig(network);
105
- // Resolve seed (use dev wallet only for local network)
106
- const walletSeed = seed ?? (network === 'local' ? Config.DEV_WALLET_SEED : undefined);
107
- if (!walletSeed) {
108
- throw new Error('Wallet seed is required for non-local networks. Provide via config.seed or MIDNIGHT_WALLET_SEED env var.');
109
- }
110
- // Initialize wallet
111
- logger.info('Initializing wallet...');
112
- const walletContext = await Wallet.init(walletSeed, networkConfig);
113
- await Wallet.waitForSync(walletContext);
114
- logger.info('Wallet synced');
115
- return {
116
- wallet: walletContext,
117
- networkConfig,
118
- logger,
119
- async contractFrom(basePath, options) {
120
- const zkConfigPath = resolvePath(basePath, options);
121
- if (!fs.existsSync(zkConfigPath)) {
122
- throw new Error(`Contract path not found: ${zkConfigPath}`);
123
- }
124
- const contractPath = path.join(zkConfigPath, 'contract', 'index.js');
125
- if (!fs.existsSync(contractPath)) {
126
- throw new Error(`Contract module not found: ${contractPath}`);
79
+ function createEffect(config) {
80
+ return Effect.gen(function* () {
81
+ const { network = 'local', networkConfig: customNetworkConfig, seed, zkConfigProvider, privateStateProvider, storage, logging = true, } = config;
82
+ const logger = createLogger(logging);
83
+ // Resolve network configuration
84
+ const networkConfig = customNetworkConfig ?? Config.getNetworkConfig(network);
85
+ // Resolve seed (use dev wallet only for local network)
86
+ const walletSeed = seed ?? (network === 'local' ? Config.DEV_WALLET_SEED : undefined);
87
+ if (!walletSeed) {
88
+ return yield* Effect.fail(new ClientError({
89
+ cause: new Error('Missing seed'),
90
+ message: 'Wallet seed is required for non-local networks. Provide via config.seed.',
91
+ }));
92
+ }
93
+ // Initialize wallet
94
+ logger.info('Initializing wallet...');
95
+ const walletContext = yield* Wallet.effect.init(walletSeed, networkConfig).pipe(Effect.mapError((e) => new ClientError({
96
+ cause: e,
97
+ message: `Failed to initialize wallet: ${e.message}`,
98
+ })));
99
+ yield* Wallet.effect.waitForSync(walletContext).pipe(Effect.mapError((e) => new ClientError({
100
+ cause: e,
101
+ message: `Failed to sync wallet: ${e.message}`,
102
+ })));
103
+ logger.info('Wallet synced');
104
+ const providerOptions = {
105
+ networkConfig,
106
+ zkConfigProvider,
107
+ privateStateProvider,
108
+ storageConfig: storage,
109
+ };
110
+ const providers = Providers.create(walletContext, providerOptions);
111
+ return {
112
+ wallet: walletContext,
113
+ networkConfig,
114
+ logger,
115
+ providers,
116
+ };
117
+ });
118
+ }
119
+ function fromWalletEffect(connection, config) {
120
+ return Effect.try({
121
+ try: () => {
122
+ const { zkConfigProvider, privateStateProvider, logging = true } = config;
123
+ const logger = createLogger(logging);
124
+ // Create network config from wallet configuration
125
+ const networkConfig = {
126
+ networkId: connection.config.networkId,
127
+ indexer: connection.config.indexerUri,
128
+ indexerWS: connection.config.indexerWsUri,
129
+ node: connection.config.substrateNodeUri,
130
+ proofServer: connection.config.proverServerUri ?? '',
131
+ };
132
+ // Create wallet providers from connection
133
+ const { walletProvider, midnightProvider } = createWalletProviders(connection.wallet, connection.addresses);
134
+ const providerOptions = {
135
+ networkConfig,
136
+ zkConfigProvider,
137
+ privateStateProvider,
138
+ };
139
+ // Create providers using the wallet providers
140
+ const providers = Providers.createFromWalletProviders(walletProvider, midnightProvider, providerOptions);
141
+ logger.info('Connected to wallet');
142
+ return {
143
+ wallet: null,
144
+ networkConfig,
145
+ logger,
146
+ providers,
147
+ };
148
+ },
149
+ catch: (cause) => new ClientError({
150
+ cause,
151
+ message: `Failed to create client from wallet: ${cause instanceof Error ? cause.message : String(cause)}`,
152
+ }),
153
+ });
154
+ }
155
+ function contractFromEffect(client, options) {
156
+ return Effect.try({
157
+ try: () => {
158
+ if (!options.module) {
159
+ throw new Error('Contract module is required. Import and pass the contract module.');
127
160
  }
128
- const contractModule = await import(contractPath);
129
161
  const module = {
130
- Contract: contractModule.Contract,
131
- ledger: contractModule.ledger,
132
- zkConfigPath,
133
- privateStateId: options?.privateStateId ?? path.basename(zkConfigPath),
134
- witnesses: options?.witnesses ?? {},
162
+ Contract: options.module.Contract,
163
+ ledger: options.module.ledger,
164
+ privateStateId: options.privateStateId ?? 'contract',
165
+ witnesses: options.witnesses ?? {},
166
+ };
167
+ return {
168
+ module,
169
+ providers: client.providers,
170
+ logger: client.logger,
135
171
  };
136
- const providers = Providers.create(walletContext, zkConfigPath, networkConfig, storage);
137
- return createContractBuilder(module, providers, logger);
138
172
  },
139
- async waitForTx(txHash) {
140
- const providers = Providers.create(walletContext, process.cwd(), networkConfig, storage);
141
- const data = await providers.publicDataProvider.watchForTxData(txHash);
173
+ catch: (cause) => new ClientError({
174
+ cause,
175
+ message: `Failed to load contract: ${cause instanceof Error ? cause.message : String(cause)}`,
176
+ }),
177
+ });
178
+ }
179
+ function waitForTxEffect(client, txHash) {
180
+ return Effect.tryPromise({
181
+ try: async () => {
182
+ const data = await client.providers.publicDataProvider.watchForTxData(txHash);
142
183
  return {
143
184
  txHash: data.txHash,
144
185
  blockHeight: data.blockHeight,
145
186
  blockHash: data.blockHash,
146
187
  };
147
188
  },
148
- };
189
+ catch: (cause) => new ClientError({
190
+ cause,
191
+ message: `Failed to wait for transaction: ${cause instanceof Error ? cause.message : String(cause)}`,
192
+ }),
193
+ });
149
194
  }
150
195
  // =============================================================================
151
- // Contract Builder
196
+ // Client - Promise API
152
197
  // =============================================================================
153
- function createContractBuilder(module, providers, logger) {
154
- return {
155
- module,
156
- async deploy(options) {
198
+ /**
199
+ * Create a Midnight client for interacting with contracts using a seed.
200
+ *
201
+ * @example
202
+ * ```typescript
203
+ * const client = await Client.create({
204
+ * seed: 'your-64-char-hex-seed',
205
+ * networkConfig: Config.NETWORKS.local,
206
+ * zkConfigProvider,
207
+ * privateStateProvider,
208
+ * });
209
+ * ```
210
+ *
211
+ * @since 0.2.0
212
+ * @category constructors
213
+ */
214
+ export async function create(config) {
215
+ return runEffectPromise(createEffect(config));
216
+ }
217
+ /**
218
+ * Create a Midnight client from a connected wallet (browser).
219
+ *
220
+ * @since 0.2.0
221
+ * @category constructors
222
+ */
223
+ export async function fromWallet(connection, config) {
224
+ return runEffectPromise(fromWalletEffect(connection, config));
225
+ }
226
+ /**
227
+ * Load a contract module for a client.
228
+ *
229
+ * @example
230
+ * ```typescript
231
+ * const builder = await Client.contractFrom(client, {
232
+ * module: await import('./contracts/counter/index.js'),
233
+ * });
234
+ * ```
235
+ *
236
+ * @since 0.2.0
237
+ * @category operations
238
+ */
239
+ export async function contractFrom(client, options) {
240
+ return runEffectPromise(contractFromEffect(client, options));
241
+ }
242
+ /**
243
+ * Wait for a transaction to be finalized.
244
+ *
245
+ * @since 0.2.0
246
+ * @category operations
247
+ */
248
+ export async function waitForTx(client, txHash) {
249
+ return runEffectPromise(waitForTxEffect(client, txHash));
250
+ }
251
+ /**
252
+ * Raw Effect APIs for advanced users.
253
+ *
254
+ * @example
255
+ * ```typescript
256
+ * const client = yield* Client.effect.create(config);
257
+ * const builder = yield* Client.effect.contractFrom(client, { module });
258
+ * ```
259
+ *
260
+ * @since 0.2.0
261
+ * @category effect
262
+ */
263
+ export const effect = {
264
+ create: createEffect,
265
+ fromWallet: fromWalletEffect,
266
+ contractFrom: contractFromEffect,
267
+ waitForTx: waitForTxEffect,
268
+ };
269
+ function deployEffect(builder, options) {
270
+ return Effect.tryPromise({
271
+ try: async () => {
157
272
  const { initialPrivateState = {} } = options ?? {};
273
+ const { module, providers, logger } = builder;
158
274
  logger.info('Deploying contract...');
159
275
  const ContractClass = module.Contract;
160
276
  const contract = new ContractClass(module.witnesses);
@@ -164,12 +280,28 @@ function createContractBuilder(module, providers, logger) {
164
280
  privateStateId: module.privateStateId,
165
281
  initialPrivateState,
166
282
  });
283
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
167
284
  const address = deployed.deployTxData.public.contractAddress;
168
285
  logger.info(`Contract deployed at: ${address}`);
169
- return createConnectedContract(address, deployed, module, providers, logger);
286
+ return {
287
+ address,
288
+ instance: deployed,
289
+ module,
290
+ providers,
291
+ logger,
292
+ };
170
293
  },
171
- async join(address, options) {
294
+ catch: (cause) => new ContractError({
295
+ cause,
296
+ message: `Failed to deploy contract: ${cause instanceof Error ? cause.message : String(cause)}`,
297
+ }),
298
+ });
299
+ }
300
+ function joinEffect(builder, address, options) {
301
+ return Effect.tryPromise({
302
+ try: async () => {
172
303
  const { initialPrivateState = {} } = options ?? {};
304
+ const { module, providers, logger } = builder;
173
305
  logger.info(`Joining contract at ${address}...`);
174
306
  const ContractClass = module.Contract;
175
307
  const contract = new ContractClass(module.witnesses);
@@ -181,18 +313,76 @@ function createContractBuilder(module, providers, logger) {
181
313
  initialPrivateState,
182
314
  });
183
315
  logger.info('Contract joined');
184
- return createConnectedContract(address, deployed, module, providers, logger);
316
+ return {
317
+ address,
318
+ instance: deployed,
319
+ module,
320
+ providers,
321
+ logger,
322
+ };
185
323
  },
186
- };
324
+ catch: (cause) => new ContractError({
325
+ cause,
326
+ message: `Failed to join contract at ${address}: ${cause instanceof Error ? cause.message : String(cause)}`,
327
+ }),
328
+ });
187
329
  }
188
- function createConnectedContract(address, instance, module, providers, logger) {
189
- return {
190
- address,
191
- instance,
192
- module,
193
- providers,
194
- logger,
195
- async call(action, ...args) {
330
+ // =============================================================================
331
+ // ContractBuilder - Promise API & Effect Namespace
332
+ // =============================================================================
333
+ /**
334
+ * ContractBuilder module functions.
335
+ *
336
+ * @since 0.2.0
337
+ * @category ContractBuilder
338
+ */
339
+ export const ContractBuilder = {
340
+ /**
341
+ * Deploy a new contract instance.
342
+ *
343
+ * @example
344
+ * ```typescript
345
+ * const contract = await ContractBuilder.deploy(builder);
346
+ * ```
347
+ *
348
+ * @since 0.2.0
349
+ * @category lifecycle
350
+ */
351
+ deploy: async (builder, options) => {
352
+ return runEffectPromise(deployEffect(builder, options));
353
+ },
354
+ /**
355
+ * Join an existing contract.
356
+ *
357
+ * @example
358
+ * ```typescript
359
+ * const contract = await ContractBuilder.join(builder, '0x...');
360
+ * ```
361
+ *
362
+ * @since 0.2.0
363
+ * @category lifecycle
364
+ */
365
+ join: async (builder, address, options) => {
366
+ return runEffectPromise(joinEffect(builder, address, options));
367
+ },
368
+ /**
369
+ * Raw Effect APIs for ContractBuilder.
370
+ *
371
+ * @since 0.2.0
372
+ * @category effect
373
+ */
374
+ effect: {
375
+ deploy: deployEffect,
376
+ join: joinEffect,
377
+ },
378
+ };
379
+ // =============================================================================
380
+ // Contract - Internal Effect Implementations
381
+ // =============================================================================
382
+ function callEffect(contract, action, ...args) {
383
+ return Effect.tryPromise({
384
+ try: async () => {
385
+ const { instance, logger } = contract;
196
386
  logger.info(`Calling ${action}()...`);
197
387
  const deployed = instance;
198
388
  const callTx = deployed.callTx;
@@ -209,14 +399,32 @@ function createConnectedContract(address, instance, module, providers, logger) {
209
399
  status: txData.public.status,
210
400
  };
211
401
  },
212
- async state() {
402
+ catch: (cause) => new ContractError({
403
+ cause,
404
+ message: `Failed to call ${action}: ${cause instanceof Error ? cause.message : String(cause)}`,
405
+ }),
406
+ });
407
+ }
408
+ function stateEffect(contract) {
409
+ return Effect.tryPromise({
410
+ try: async () => {
411
+ const { address, providers } = contract;
213
412
  const contractState = await providers.publicDataProvider.queryContractState(address);
214
413
  if (!contractState) {
215
414
  throw new Error(`Contract state not found at ${address}`);
216
415
  }
217
416
  return contractState.data;
218
417
  },
219
- async stateAt(blockHeight) {
418
+ catch: (cause) => new ContractError({
419
+ cause,
420
+ message: `Failed to query contract state: ${cause instanceof Error ? cause.message : String(cause)}`,
421
+ }),
422
+ });
423
+ }
424
+ function stateAtEffect(contract, blockHeight) {
425
+ return Effect.tryPromise({
426
+ try: async () => {
427
+ const { address, providers } = contract;
220
428
  const contractState = await providers.publicDataProvider.queryContractState(address, {
221
429
  type: 'blockHeight',
222
430
  blockHeight,
@@ -226,14 +434,271 @@ function createConnectedContract(address, instance, module, providers, logger) {
226
434
  }
227
435
  return contractState.data;
228
436
  },
229
- async ledgerState() {
230
- const data = await this.state();
231
- return module.ledger(data);
232
- },
233
- async ledgerStateAt(blockHeight) {
234
- const data = await this.stateAt(blockHeight);
235
- return module.ledger(data);
236
- },
237
- };
437
+ catch: (cause) => new ContractError({
438
+ cause,
439
+ message: `Failed to query contract state at block ${blockHeight}: ${cause instanceof Error ? cause.message : String(cause)}`,
440
+ }),
441
+ });
442
+ }
443
+ function ledgerStateEffect(contract) {
444
+ return stateEffect(contract).pipe(Effect.map((data) => contract.module.ledger(data)));
445
+ }
446
+ function ledgerStateAtEffect(contract, blockHeight) {
447
+ return stateAtEffect(contract, blockHeight).pipe(Effect.map((data) => contract.module.ledger(data)));
448
+ }
449
+ // =============================================================================
450
+ // Contract - Promise API & Effect Namespace
451
+ // =============================================================================
452
+ /**
453
+ * Contract module functions.
454
+ *
455
+ * @since 0.2.0
456
+ * @category Contract
457
+ */
458
+ export const Contract = {
459
+ /**
460
+ * Call a contract action.
461
+ *
462
+ * @example
463
+ * ```typescript
464
+ * const result = await Contract.call(contract, 'increment');
465
+ * ```
466
+ *
467
+ * @since 0.2.0
468
+ * @category operations
469
+ */
470
+ call: async (contract, action, ...args) => {
471
+ return runEffectPromise(callEffect(contract, action, ...args));
472
+ },
473
+ /**
474
+ * Get contract state.
475
+ *
476
+ * @since 0.2.0
477
+ * @category inspection
478
+ */
479
+ state: async (contract) => {
480
+ return runEffectPromise(stateEffect(contract));
481
+ },
482
+ /**
483
+ * Get contract state at a specific block height.
484
+ *
485
+ * @since 0.2.0
486
+ * @category inspection
487
+ */
488
+ stateAt: async (contract, blockHeight) => {
489
+ return runEffectPromise(stateAtEffect(contract, blockHeight));
490
+ },
491
+ /**
492
+ * Get ledger state (parsed through ledger function).
493
+ *
494
+ * @since 0.2.0
495
+ * @category inspection
496
+ */
497
+ ledgerState: async (contract) => {
498
+ return runEffectPromise(ledgerStateEffect(contract));
499
+ },
500
+ /**
501
+ * Get ledger state at a specific block height.
502
+ *
503
+ * @since 0.2.0
504
+ * @category inspection
505
+ */
506
+ ledgerStateAt: async (contract, blockHeight) => {
507
+ return runEffectPromise(ledgerStateAtEffect(contract, blockHeight));
508
+ },
509
+ /**
510
+ * Raw Effect APIs for Contract.
511
+ *
512
+ * @since 0.2.0
513
+ * @category effect
514
+ */
515
+ effect: {
516
+ call: callEffect,
517
+ state: stateEffect,
518
+ stateAt: stateAtEffect,
519
+ ledgerState: ledgerStateEffect,
520
+ ledgerStateAt: ledgerStateAtEffect,
521
+ },
522
+ };
523
+ /**
524
+ * Context.Tag for ClientService dependency injection.
525
+ *
526
+ * @example
527
+ * ```typescript
528
+ * const program = Effect.gen(function* () {
529
+ * const clientService = yield* ClientService;
530
+ * const client = yield* clientService.create(config);
531
+ * return client;
532
+ * });
533
+ *
534
+ * Effect.runPromise(program.pipe(Effect.provide(ClientLive)));
535
+ * ```
536
+ *
537
+ * @since 0.2.0
538
+ * @category service
539
+ */
540
+ export class ClientService extends Context.Tag('ClientService')() {
541
+ }
542
+ /**
543
+ * Context.Tag for ContractBuilderService dependency injection.
544
+ *
545
+ * @since 0.2.0
546
+ * @category service
547
+ */
548
+ export class ContractBuilderService extends Context.Tag('ContractBuilderService')() {
549
+ }
550
+ /**
551
+ * Context.Tag for ContractService dependency injection.
552
+ *
553
+ * @since 0.2.0
554
+ * @category service
555
+ */
556
+ export class ContractService extends Context.Tag('ContractService')() {
557
+ }
558
+ // =============================================================================
559
+ // Effect DI - Live Layers
560
+ // =============================================================================
561
+ /**
562
+ * Live Layer for ClientService.
563
+ *
564
+ * @since 0.2.0
565
+ * @category layer
566
+ */
567
+ export const ClientLive = Layer.succeed(ClientService, {
568
+ create: createEffect,
569
+ fromWallet: fromWalletEffect,
570
+ contractFrom: contractFromEffect,
571
+ waitForTx: waitForTxEffect,
572
+ });
573
+ /**
574
+ * Live Layer for ContractBuilderService.
575
+ *
576
+ * @since 0.2.0
577
+ * @category layer
578
+ */
579
+ export const ContractBuilderLive = Layer.succeed(ContractBuilderService, {
580
+ deploy: deployEffect,
581
+ join: joinEffect,
582
+ });
583
+ /**
584
+ * Live Layer for ContractService.
585
+ *
586
+ * @since 0.2.0
587
+ * @category layer
588
+ */
589
+ export const ContractLive = Layer.succeed(ContractService, {
590
+ call: callEffect,
591
+ state: stateEffect,
592
+ stateAt: stateAtEffect,
593
+ ledgerState: ledgerStateEffect,
594
+ ledgerStateAt: ledgerStateAtEffect,
595
+ });
596
+ // =============================================================================
597
+ // Layer Factories
598
+ // =============================================================================
599
+ /**
600
+ * Create a Layer providing all Client-related factory services.
601
+ *
602
+ * Use this when you want to create clients on-demand within your Effect programs.
603
+ * For pre-initialized clients, use `Client.layer(config)` instead.
604
+ *
605
+ * @example
606
+ * ```typescript
607
+ * import { Effect } from 'effect';
608
+ * import * as Midday from '@no-witness-labs/midday-sdk';
609
+ *
610
+ * const program = Effect.gen(function* () {
611
+ * const clientService = yield* Midday.ClientService;
612
+ * const client = yield* clientService.create(config);
613
+ * return client;
614
+ * });
615
+ *
616
+ * await Effect.runPromise(program.pipe(Effect.provide(Midday.Client.services())));
617
+ * ```
618
+ *
619
+ * @since 0.3.0
620
+ * @category layer
621
+ */
622
+ export function services() {
623
+ return Layer.mergeAll(ClientLive, ContractBuilderLive, ContractLive);
624
+ }
625
+ // =============================================================================
626
+ // Pre-configured Client Layer
627
+ // =============================================================================
628
+ /**
629
+ * Context.Tag for a pre-initialized MidnightClient.
630
+ *
631
+ * Use with `Client.layer(config)` for dependency injection of a configured client.
632
+ *
633
+ * @since 0.3.0
634
+ * @category service
635
+ */
636
+ export class MidnightClientService extends Context.Tag('MidnightClientService')() {
637
+ }
638
+ /**
639
+ * Create a Layer that provides a pre-initialized MidnightClient.
640
+ *
641
+ * This is the recommended way to inject a client into Effect programs
642
+ * when you have a known configuration at startup. Follows the same pattern
643
+ * as `Cluster.layer(config)`.
644
+ *
645
+ * @example
646
+ * ```typescript
647
+ * import { Effect } from 'effect';
648
+ * import * as Midday from '@no-witness-labs/midday-sdk';
649
+ *
650
+ * const clientLayer = Midday.Client.layer({
651
+ * seed: 'your-64-char-hex-seed',
652
+ * networkConfig: Midday.Config.NETWORKS.local,
653
+ * zkConfigProvider: new Midday.HttpZkConfigProvider('http://localhost:3000/zk'),
654
+ * privateStateProvider: Midday.inMemoryPrivateStateProvider(),
655
+ * });
656
+ *
657
+ * const program = Effect.gen(function* () {
658
+ * const client = yield* Midday.MidnightClientService;
659
+ * const builder = yield* Midday.Client.effect.contractFrom(client, { module });
660
+ * return builder;
661
+ * });
662
+ *
663
+ * await Effect.runPromise(program.pipe(Effect.provide(clientLayer)));
664
+ * ```
665
+ *
666
+ * @since 0.3.0
667
+ * @category layer
668
+ */
669
+ export function layer(config) {
670
+ return Layer.effect(MidnightClientService, createEffect(config));
671
+ }
672
+ /**
673
+ * Create a Layer that provides a pre-initialized MidnightClient from a wallet connection.
674
+ *
675
+ * Use this for browser environments with Lace wallet integration.
676
+ *
677
+ * @example
678
+ * ```typescript
679
+ * import { Effect } from 'effect';
680
+ * import * as Midday from '@no-witness-labs/midday-sdk';
681
+ *
682
+ * // After connecting wallet
683
+ * const connection = await Midday.connectWallet('testnet');
684
+ *
685
+ * const clientLayer = Midday.Client.layerFromWallet(connection, {
686
+ * zkConfigProvider: new Midday.HttpZkConfigProvider('https://cdn.example.com/zk'),
687
+ * privateStateProvider: Midday.indexedDBPrivateStateProvider({ privateStateStoreName: 'my-app' }),
688
+ * });
689
+ *
690
+ * const program = Effect.gen(function* () {
691
+ * const client = yield* Midday.MidnightClientService;
692
+ * // Use client...
693
+ * });
694
+ *
695
+ * await Effect.runPromise(program.pipe(Effect.provide(clientLayer)));
696
+ * ```
697
+ *
698
+ * @since 0.3.0
699
+ * @category layer
700
+ */
701
+ export function layerFromWallet(connection, config) {
702
+ return Layer.effect(MidnightClientService, fromWalletEffect(connection, config));
238
703
  }
239
704
  //# sourceMappingURL=Client.js.map