@no-witness-labs/midday-sdk 0.1.2 → 0.2.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.
Files changed (94) hide show
  1. package/README.md +145 -78
  2. package/dist/Client.d.ts +464 -72
  3. package/dist/Client.d.ts.map +1 -1
  4. package/dist/Client.js +526 -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/devnet/Cluster.d.ts +282 -0
  19. package/dist/devnet/Cluster.d.ts.map +1 -0
  20. package/dist/devnet/Cluster.js +487 -0
  21. package/dist/devnet/Cluster.js.map +1 -0
  22. package/dist/devnet/Config.d.ts +119 -0
  23. package/dist/devnet/Config.d.ts.map +1 -0
  24. package/dist/devnet/Config.js +75 -0
  25. package/dist/devnet/Config.js.map +1 -0
  26. package/dist/devnet/Container.d.ts +180 -0
  27. package/dist/devnet/Container.d.ts.map +1 -0
  28. package/dist/devnet/Container.js +390 -0
  29. package/dist/devnet/Container.js.map +1 -0
  30. package/dist/devnet/Health.d.ts +129 -0
  31. package/dist/devnet/Health.d.ts.map +1 -0
  32. package/dist/devnet/Health.js +304 -0
  33. package/dist/devnet/Health.js.map +1 -0
  34. package/dist/devnet/Images.d.ts +43 -0
  35. package/dist/devnet/Images.d.ts.map +1 -0
  36. package/dist/devnet/Images.js +96 -0
  37. package/dist/devnet/Images.js.map +1 -0
  38. package/dist/devnet/errors.d.ts +65 -0
  39. package/dist/devnet/errors.d.ts.map +1 -0
  40. package/dist/devnet/errors.js +40 -0
  41. package/dist/devnet/errors.js.map +1 -0
  42. package/dist/devnet/index.d.ts +72 -0
  43. package/dist/devnet/index.d.ts.map +1 -0
  44. package/dist/devnet/index.js +73 -0
  45. package/dist/devnet/index.js.map +1 -0
  46. package/dist/index.d.ts +63 -7
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +68 -4
  49. package/dist/index.js.map +1 -1
  50. package/dist/providers/HttpZkConfigProvider.d.ts +229 -0
  51. package/dist/providers/HttpZkConfigProvider.d.ts.map +1 -0
  52. package/dist/providers/HttpZkConfigProvider.js +275 -0
  53. package/dist/providers/HttpZkConfigProvider.js.map +1 -0
  54. package/dist/providers/IndexedDBPrivateStateProvider.d.ts +270 -0
  55. package/dist/providers/IndexedDBPrivateStateProvider.d.ts.map +1 -0
  56. package/dist/providers/IndexedDBPrivateStateProvider.js +513 -0
  57. package/dist/providers/IndexedDBPrivateStateProvider.js.map +1 -0
  58. package/dist/providers/errors.d.ts +50 -0
  59. package/dist/providers/errors.d.ts.map +1 -0
  60. package/dist/providers/errors.js +32 -0
  61. package/dist/providers/errors.js.map +1 -0
  62. package/dist/sdk/Type.d.ts +91 -0
  63. package/dist/sdk/Type.d.ts.map +1 -0
  64. package/dist/sdk/Type.js +8 -0
  65. package/dist/sdk/Type.js.map +1 -0
  66. package/dist/utils/address.d.ts +56 -0
  67. package/dist/utils/address.d.ts.map +1 -0
  68. package/dist/utils/address.js +135 -0
  69. package/dist/utils/address.js.map +1 -0
  70. package/dist/utils/coin.d.ts +55 -0
  71. package/dist/utils/coin.d.ts.map +1 -0
  72. package/dist/utils/coin.js +84 -0
  73. package/dist/utils/coin.js.map +1 -0
  74. package/dist/utils/effect-runtime.d.ts +66 -0
  75. package/dist/utils/effect-runtime.d.ts.map +1 -0
  76. package/dist/utils/effect-runtime.js +147 -0
  77. package/dist/utils/effect-runtime.js.map +1 -0
  78. package/dist/utils/hex.d.ts +62 -0
  79. package/dist/utils/hex.d.ts.map +1 -0
  80. package/dist/utils/hex.js +93 -0
  81. package/dist/utils/hex.js.map +1 -0
  82. package/dist/wallet/connector.d.ts +191 -0
  83. package/dist/wallet/connector.d.ts.map +1 -0
  84. package/dist/wallet/connector.js +183 -0
  85. package/dist/wallet/connector.js.map +1 -0
  86. package/dist/wallet/errors.d.ts +22 -0
  87. package/dist/wallet/errors.d.ts.map +1 -0
  88. package/dist/wallet/errors.js +16 -0
  89. package/dist/wallet/errors.js.map +1 -0
  90. package/dist/wallet/provider.d.ts +102 -0
  91. package/dist/wallet/provider.d.ts.map +1 -0
  92. package/dist/wallet/provider.js +139 -0
  93. package/dist/wallet/provider.js.map +1 -0
  94. package/package.json +23 -8
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,189 @@ 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 services.
601
+ *
602
+ * @example
603
+ * ```typescript
604
+ * import { Effect } from 'effect';
605
+ * import * as Midday from '@no-witness-labs/midday-sdk';
606
+ *
607
+ * const program = Effect.gen(function* () {
608
+ * const clientService = yield* Midday.ClientService;
609
+ * const client = yield* clientService.create(config);
610
+ * return client;
611
+ * });
612
+ *
613
+ * await Effect.runPromise(program.pipe(Effect.provide(Midday.Client.layer())));
614
+ * ```
615
+ *
616
+ * @since 0.3.0
617
+ * @category layer
618
+ */
619
+ export function layer() {
620
+ return Layer.mergeAll(ClientLive, ContractBuilderLive, ContractLive);
238
621
  }
239
622
  //# sourceMappingURL=Client.js.map