@no-witness-labs/midday-sdk 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -82
- package/dist/Client.d.ts +290 -340
- package/dist/Client.d.ts.map +1 -1
- package/dist/Client.js +318 -359
- package/dist/Client.js.map +1 -1
- package/dist/Config.d.ts +2 -57
- package/dist/Config.d.ts.map +1 -1
- package/dist/Config.js +1 -47
- package/dist/Config.js.map +1 -1
- package/dist/Hash.d.ts +126 -0
- package/dist/Hash.d.ts.map +1 -0
- package/dist/Hash.js +146 -0
- package/dist/Hash.js.map +1 -0
- package/dist/PrivateState.d.ts +9 -0
- package/dist/PrivateState.d.ts.map +1 -0
- package/dist/PrivateState.js +9 -0
- package/dist/PrivateState.js.map +1 -0
- package/dist/Providers.d.ts +42 -63
- package/dist/Providers.d.ts.map +1 -1
- package/dist/Providers.js +34 -62
- package/dist/Providers.js.map +1 -1
- package/dist/Runtime.d.ts +8 -0
- package/dist/Runtime.d.ts.map +1 -0
- package/dist/Runtime.js +8 -0
- package/dist/Runtime.js.map +1 -0
- package/dist/Wallet.d.ts +1 -1
- package/dist/Wallet.d.ts.map +1 -1
- package/dist/Wallet.js +2 -0
- package/dist/Wallet.js.map +1 -1
- package/dist/ZkConfig.d.ts +80 -0
- package/dist/ZkConfig.d.ts.map +1 -0
- package/dist/ZkConfig.js +85 -0
- package/dist/ZkConfig.js.map +1 -0
- package/dist/devnet/Cluster.d.ts +0 -9
- package/dist/devnet/Cluster.d.ts.map +1 -1
- package/dist/devnet/Cluster.js +0 -13
- package/dist/devnet/Cluster.js.map +1 -1
- package/dist/devnet/index.d.ts +9 -8
- package/dist/devnet/index.d.ts.map +1 -1
- package/dist/devnet/index.js +9 -8
- package/dist/devnet/index.js.map +1 -1
- package/dist/index.d.ts +30 -47
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -52
- package/dist/index.js.map +1 -1
- package/dist/providers/HttpZkConfigProvider.d.ts +8 -6
- package/dist/providers/HttpZkConfigProvider.d.ts.map +1 -1
- package/dist/providers/HttpZkConfigProvider.js +8 -6
- package/dist/providers/HttpZkConfigProvider.js.map +1 -1
- package/dist/providers/IndexedDBPrivateStateProvider.d.ts +8 -8
- package/dist/providers/IndexedDBPrivateStateProvider.js +8 -8
- package/dist/utils/index.d.ts +9 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +9 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/wallet/connector.d.ts +1 -1
- package/dist/wallet/connector.js +1 -1
- package/dist/wallet/index.d.ts +15 -0
- package/dist/wallet/index.d.ts.map +1 -0
- package/dist/wallet/index.js +18 -0
- package/dist/wallet/index.js.map +1 -0
- package/package.json +1 -1
- package/dist/sdk/Type.d.ts +0 -91
- package/dist/sdk/Type.d.ts.map +0 -1
- package/dist/sdk/Type.js +0 -8
- package/dist/sdk/Type.js.map +0 -1
package/dist/Client.js
CHANGED
|
@@ -3,26 +3,31 @@
|
|
|
3
3
|
*
|
|
4
4
|
* ## API Design
|
|
5
5
|
*
|
|
6
|
-
* This module uses a **
|
|
6
|
+
* This module uses a **Client-centric hub pattern** following the Effect hybrid pattern:
|
|
7
7
|
*
|
|
8
|
-
* - **
|
|
9
|
-
* - **
|
|
10
|
-
* - **
|
|
8
|
+
* - **Effect is source of truth**: All logic in Effect functions
|
|
9
|
+
* - **Client is the hub**: All operations flow from the client
|
|
10
|
+
* - **Two interfaces**: `.effect.method()` for Effect users, `.method()` for Promise users
|
|
11
|
+
* - **Effects call Effects, Promises call Promises**: Never mix execution models
|
|
11
12
|
*
|
|
12
13
|
* ### Usage Patterns
|
|
13
14
|
*
|
|
14
15
|
* ```typescript
|
|
15
|
-
* // Promise user
|
|
16
|
-
* const client = await Client.create(config);
|
|
17
|
-
* const
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* const
|
|
24
|
-
*
|
|
25
|
-
*
|
|
16
|
+
* // Promise user - simple flow
|
|
17
|
+
* const client = await Midday.Client.create(config);
|
|
18
|
+
* const contract = await client.loadContract({ path: './contracts/counter' });
|
|
19
|
+
* await contract.deploy();
|
|
20
|
+
* await contract.call('increment');
|
|
21
|
+
* const state = await contract.ledgerState();
|
|
22
|
+
*
|
|
23
|
+
* // Effect user - compositional
|
|
24
|
+
* const program = Effect.gen(function* () {
|
|
25
|
+
* const client = yield* Midday.Client.effect.create(config);
|
|
26
|
+
* const contract = yield* client.effect.loadContract({ path: './contracts/counter' });
|
|
27
|
+
* yield* contract.effect.deploy();
|
|
28
|
+
* yield* contract.effect.call('increment');
|
|
29
|
+
* const state = yield* contract.effect.ledgerState();
|
|
30
|
+
* });
|
|
26
31
|
* ```
|
|
27
32
|
*
|
|
28
33
|
* @since 0.1.0
|
|
@@ -55,11 +60,11 @@ export class ClientError extends Data.TaggedError('ClientError') {
|
|
|
55
60
|
export class ContractError extends Data.TaggedError('ContractError') {
|
|
56
61
|
}
|
|
57
62
|
// =============================================================================
|
|
58
|
-
//
|
|
63
|
+
// Effect Implementations (Source of Truth)
|
|
59
64
|
// =============================================================================
|
|
60
|
-
function
|
|
65
|
+
function createClientDataEffect(config) {
|
|
61
66
|
return Effect.gen(function* () {
|
|
62
|
-
const { network = 'local', networkConfig: customNetworkConfig, seed,
|
|
67
|
+
const { network = 'local', networkConfig: customNetworkConfig, seed, privateStateProvider, storage, logging = true, } = config;
|
|
63
68
|
// Resolve network configuration
|
|
64
69
|
const networkConfig = customNetworkConfig ?? Config.getNetworkConfig(network);
|
|
65
70
|
// Resolve seed (use dev wallet only for local network)
|
|
@@ -81,13 +86,13 @@ function createEffect(config) {
|
|
|
81
86
|
message: `Failed to sync wallet: ${e.message}`,
|
|
82
87
|
})));
|
|
83
88
|
yield* Effect.logDebug('Wallet synced');
|
|
89
|
+
// Create base providers (no zkConfig - that's per-contract)
|
|
84
90
|
const providerOptions = {
|
|
85
91
|
networkConfig,
|
|
86
|
-
zkConfigProvider,
|
|
87
92
|
privateStateProvider,
|
|
88
93
|
storageConfig: storage,
|
|
89
94
|
};
|
|
90
|
-
const providers = Providers.
|
|
95
|
+
const providers = Providers.createBase(walletContext, providerOptions);
|
|
91
96
|
return {
|
|
92
97
|
wallet: walletContext,
|
|
93
98
|
networkConfig,
|
|
@@ -96,9 +101,9 @@ function createEffect(config) {
|
|
|
96
101
|
};
|
|
97
102
|
});
|
|
98
103
|
}
|
|
99
|
-
function
|
|
104
|
+
function fromWalletDataEffect(connection, config) {
|
|
100
105
|
return Effect.gen(function* () {
|
|
101
|
-
const {
|
|
106
|
+
const { privateStateProvider, logging = true } = config;
|
|
102
107
|
// Create network config from wallet configuration
|
|
103
108
|
const networkConfig = {
|
|
104
109
|
networkId: connection.config.networkId,
|
|
@@ -109,13 +114,13 @@ function fromWalletEffect(connection, config) {
|
|
|
109
114
|
};
|
|
110
115
|
// Create wallet providers from connection
|
|
111
116
|
const { walletProvider, midnightProvider } = createWalletProviders(connection.wallet, connection.addresses);
|
|
117
|
+
// Create base providers (no zkConfig - that's per-contract)
|
|
112
118
|
const providerOptions = {
|
|
113
119
|
networkConfig,
|
|
114
|
-
zkConfigProvider,
|
|
115
120
|
privateStateProvider,
|
|
116
121
|
};
|
|
117
122
|
// Create providers using the wallet providers
|
|
118
|
-
const providers = Providers.
|
|
123
|
+
const providers = Providers.createBaseFromWalletProviders(walletProvider, midnightProvider, providerOptions);
|
|
119
124
|
yield* Effect.logDebug('Connected to wallet');
|
|
120
125
|
return {
|
|
121
126
|
wallet: null,
|
|
@@ -128,22 +133,57 @@ function fromWalletEffect(connection, config) {
|
|
|
128
133
|
message: `Failed to create client from wallet: ${defect instanceof Error ? defect.message : String(defect)}`,
|
|
129
134
|
}))));
|
|
130
135
|
}
|
|
131
|
-
function
|
|
132
|
-
return Effect.
|
|
133
|
-
try: () => {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
+
function loadContractEffect(clientData, options) {
|
|
137
|
+
return Effect.tryPromise({
|
|
138
|
+
try: async () => {
|
|
139
|
+
let module;
|
|
140
|
+
let zkConfig;
|
|
141
|
+
// Determine loading method
|
|
142
|
+
if (options.path) {
|
|
143
|
+
// Path-based loading (Node.js)
|
|
144
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
145
|
+
const { join } = require('path');
|
|
146
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
147
|
+
const { NodeZkConfigProvider } = require('@midnight-ntwrk/midnight-js-node-zk-config-provider');
|
|
148
|
+
const modulePath = join(options.path, 'contract', 'index.js');
|
|
149
|
+
module = await import(modulePath);
|
|
150
|
+
zkConfig = new NodeZkConfigProvider(options.path);
|
|
151
|
+
}
|
|
152
|
+
else if (options.moduleUrl && options.zkConfigBaseUrl) {
|
|
153
|
+
// URL-based loading (browser)
|
|
154
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
155
|
+
const { HttpZkConfigProvider } = require('./providers/HttpZkConfigProvider.js');
|
|
156
|
+
module = await import(/* webpackIgnore: true */ options.moduleUrl);
|
|
157
|
+
zkConfig = new HttpZkConfigProvider(options.zkConfigBaseUrl);
|
|
158
|
+
}
|
|
159
|
+
else if (options.module && options.zkConfig) {
|
|
160
|
+
// Direct loading
|
|
161
|
+
module = options.module;
|
|
162
|
+
zkConfig = options.zkConfig;
|
|
136
163
|
}
|
|
137
|
-
|
|
138
|
-
Contract:
|
|
139
|
-
|
|
164
|
+
else {
|
|
165
|
+
throw new Error('Contract loading requires one of: ' +
|
|
166
|
+
'(1) module + zkConfig, ' +
|
|
167
|
+
'(2) path (Node.js), or ' +
|
|
168
|
+
'(3) moduleUrl + zkConfigBaseUrl (browser)');
|
|
169
|
+
}
|
|
170
|
+
const loadedModule = {
|
|
171
|
+
Contract: module.Contract,
|
|
172
|
+
ledger: module.ledger,
|
|
140
173
|
privateStateId: options.privateStateId ?? 'contract',
|
|
141
174
|
witnesses: options.witnesses ?? {},
|
|
142
175
|
};
|
|
176
|
+
// Create full providers with zkConfig for this contract
|
|
177
|
+
const providers = {
|
|
178
|
+
...clientData.providers,
|
|
179
|
+
zkConfigProvider: zkConfig,
|
|
180
|
+
};
|
|
143
181
|
return {
|
|
144
|
-
module,
|
|
145
|
-
providers
|
|
146
|
-
logging:
|
|
182
|
+
module: loadedModule,
|
|
183
|
+
providers,
|
|
184
|
+
logging: clientData.logging,
|
|
185
|
+
address: undefined,
|
|
186
|
+
instance: undefined,
|
|
147
187
|
};
|
|
148
188
|
},
|
|
149
189
|
catch: (cause) => new ClientError({
|
|
@@ -152,10 +192,10 @@ function contractFromEffect(client, options) {
|
|
|
152
192
|
}),
|
|
153
193
|
});
|
|
154
194
|
}
|
|
155
|
-
function waitForTxEffect(
|
|
195
|
+
function waitForTxEffect(clientData, txHash) {
|
|
156
196
|
return Effect.tryPromise({
|
|
157
197
|
try: async () => {
|
|
158
|
-
const data = await
|
|
198
|
+
const data = await clientData.providers.publicDataProvider.watchForTxData(txHash);
|
|
159
199
|
return {
|
|
160
200
|
txHash: data.txHash,
|
|
161
201
|
blockHeight: data.blockHeight,
|
|
@@ -168,86 +208,17 @@ function waitForTxEffect(client, txHash) {
|
|
|
168
208
|
}),
|
|
169
209
|
});
|
|
170
210
|
}
|
|
171
|
-
|
|
172
|
-
// Client - Promise API
|
|
173
|
-
// =============================================================================
|
|
174
|
-
/**
|
|
175
|
-
* Create a Midnight client for interacting with contracts using a seed.
|
|
176
|
-
*
|
|
177
|
-
* @example
|
|
178
|
-
* ```typescript
|
|
179
|
-
* const client = await Client.create({
|
|
180
|
-
* seed: 'your-64-char-hex-seed',
|
|
181
|
-
* networkConfig: Config.NETWORKS.local,
|
|
182
|
-
* zkConfigProvider,
|
|
183
|
-
* privateStateProvider,
|
|
184
|
-
* });
|
|
185
|
-
* ```
|
|
186
|
-
*
|
|
187
|
-
* @since 0.2.0
|
|
188
|
-
* @category constructors
|
|
189
|
-
*/
|
|
190
|
-
export async function create(config) {
|
|
191
|
-
const logging = config.logging ?? true;
|
|
192
|
-
return runEffectWithLogging(createEffect(config), logging);
|
|
193
|
-
}
|
|
194
|
-
/**
|
|
195
|
-
* Create a Midnight client from a connected wallet (browser).
|
|
196
|
-
*
|
|
197
|
-
* @since 0.2.0
|
|
198
|
-
* @category constructors
|
|
199
|
-
*/
|
|
200
|
-
export async function fromWallet(connection, config) {
|
|
201
|
-
const logging = config.logging ?? true;
|
|
202
|
-
return runEffectWithLogging(fromWalletEffect(connection, config), logging);
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* Load a contract module for a client.
|
|
206
|
-
*
|
|
207
|
-
* @example
|
|
208
|
-
* ```typescript
|
|
209
|
-
* const builder = await Client.contractFrom(client, {
|
|
210
|
-
* module: await import('./contracts/counter/index.js'),
|
|
211
|
-
* });
|
|
212
|
-
* ```
|
|
213
|
-
*
|
|
214
|
-
* @since 0.2.0
|
|
215
|
-
* @category operations
|
|
216
|
-
*/
|
|
217
|
-
export async function contractFrom(client, options) {
|
|
218
|
-
return runEffectWithLogging(contractFromEffect(client, options), client.logging);
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
|
-
* Wait for a transaction to be finalized.
|
|
222
|
-
*
|
|
223
|
-
* @since 0.2.0
|
|
224
|
-
* @category operations
|
|
225
|
-
*/
|
|
226
|
-
export async function waitForTx(client, txHash) {
|
|
227
|
-
return runEffectWithLogging(waitForTxEffect(client, txHash), client.logging);
|
|
228
|
-
}
|
|
229
|
-
/**
|
|
230
|
-
* Raw Effect APIs for advanced users.
|
|
231
|
-
*
|
|
232
|
-
* @example
|
|
233
|
-
* ```typescript
|
|
234
|
-
* const client = yield* Client.effect.create(config);
|
|
235
|
-
* const builder = yield* Client.effect.contractFrom(client, { module });
|
|
236
|
-
* ```
|
|
237
|
-
*
|
|
238
|
-
* @since 0.2.0
|
|
239
|
-
* @category effect
|
|
240
|
-
*/
|
|
241
|
-
export const effect = {
|
|
242
|
-
create: createEffect,
|
|
243
|
-
fromWallet: fromWalletEffect,
|
|
244
|
-
contractFrom: contractFromEffect,
|
|
245
|
-
waitForTx: waitForTxEffect,
|
|
246
|
-
};
|
|
247
|
-
function deployEffect(builder, options) {
|
|
211
|
+
function deployContractEffect(contractData, options) {
|
|
248
212
|
return Effect.gen(function* () {
|
|
213
|
+
// State machine check
|
|
214
|
+
if (contractData.address !== undefined) {
|
|
215
|
+
return yield* Effect.fail(new ContractError({
|
|
216
|
+
cause: new Error('Already deployed'),
|
|
217
|
+
message: `Contract already deployed at ${contractData.address}. Cannot deploy again.`,
|
|
218
|
+
}));
|
|
219
|
+
}
|
|
249
220
|
const { initialPrivateState = {} } = options ?? {};
|
|
250
|
-
const { module, providers, logging } =
|
|
221
|
+
const { module, providers, logging } = contractData;
|
|
251
222
|
yield* Effect.logDebug('Deploying contract...');
|
|
252
223
|
const ContractClass = module.Contract;
|
|
253
224
|
const contract = new ContractClass(module.witnesses);
|
|
@@ -277,10 +248,17 @@ function deployEffect(builder, options) {
|
|
|
277
248
|
};
|
|
278
249
|
});
|
|
279
250
|
}
|
|
280
|
-
function
|
|
251
|
+
function joinContractEffect(contractData, address, options) {
|
|
281
252
|
return Effect.gen(function* () {
|
|
253
|
+
// State machine check
|
|
254
|
+
if (contractData.address !== undefined) {
|
|
255
|
+
return yield* Effect.fail(new ContractError({
|
|
256
|
+
cause: new Error('Already deployed'),
|
|
257
|
+
message: `Contract already connected at ${contractData.address}. Cannot join another.`,
|
|
258
|
+
}));
|
|
259
|
+
}
|
|
282
260
|
const { initialPrivateState = {} } = options ?? {};
|
|
283
|
-
const { module, providers, logging } =
|
|
261
|
+
const { module, providers, logging } = contractData;
|
|
284
262
|
yield* Effect.logDebug(`Joining contract at ${address}...`);
|
|
285
263
|
const ContractClass = module.Contract;
|
|
286
264
|
const contract = new ContractClass(module.witnesses);
|
|
@@ -309,61 +287,16 @@ function joinEffect(builder, address, options) {
|
|
|
309
287
|
};
|
|
310
288
|
});
|
|
311
289
|
}
|
|
312
|
-
|
|
313
|
-
// ContractBuilder - Promise API & Effect Namespace
|
|
314
|
-
// =============================================================================
|
|
315
|
-
/**
|
|
316
|
-
* ContractBuilder module functions.
|
|
317
|
-
*
|
|
318
|
-
* @since 0.2.0
|
|
319
|
-
* @category ContractBuilder
|
|
320
|
-
*/
|
|
321
|
-
export const ContractBuilder = {
|
|
322
|
-
/**
|
|
323
|
-
* Deploy a new contract instance.
|
|
324
|
-
*
|
|
325
|
-
* @example
|
|
326
|
-
* ```typescript
|
|
327
|
-
* const contract = await ContractBuilder.deploy(builder);
|
|
328
|
-
* ```
|
|
329
|
-
*
|
|
330
|
-
* @since 0.2.0
|
|
331
|
-
* @category lifecycle
|
|
332
|
-
*/
|
|
333
|
-
deploy: async (builder, options) => {
|
|
334
|
-
return runEffectWithLogging(deployEffect(builder, options), builder.logging);
|
|
335
|
-
},
|
|
336
|
-
/**
|
|
337
|
-
* Join an existing contract.
|
|
338
|
-
*
|
|
339
|
-
* @example
|
|
340
|
-
* ```typescript
|
|
341
|
-
* const contract = await ContractBuilder.join(builder, '0x...');
|
|
342
|
-
* ```
|
|
343
|
-
*
|
|
344
|
-
* @since 0.2.0
|
|
345
|
-
* @category lifecycle
|
|
346
|
-
*/
|
|
347
|
-
join: async (builder, address, options) => {
|
|
348
|
-
return runEffectWithLogging(joinEffect(builder, address, options), builder.logging);
|
|
349
|
-
},
|
|
350
|
-
/**
|
|
351
|
-
* Raw Effect APIs for ContractBuilder.
|
|
352
|
-
*
|
|
353
|
-
* @since 0.2.0
|
|
354
|
-
* @category effect
|
|
355
|
-
*/
|
|
356
|
-
effect: {
|
|
357
|
-
deploy: deployEffect,
|
|
358
|
-
join: joinEffect,
|
|
359
|
-
},
|
|
360
|
-
};
|
|
361
|
-
// =============================================================================
|
|
362
|
-
// Contract - Internal Effect Implementations
|
|
363
|
-
// =============================================================================
|
|
364
|
-
function callEffect(contract, action, ...args) {
|
|
290
|
+
function callContractEffect(contractData, action, ...args) {
|
|
365
291
|
return Effect.gen(function* () {
|
|
366
|
-
|
|
292
|
+
// State machine check
|
|
293
|
+
if (contractData.instance === undefined) {
|
|
294
|
+
return yield* Effect.fail(new ContractError({
|
|
295
|
+
cause: new Error('Not deployed'),
|
|
296
|
+
message: `Contract not deployed. Call deploy() or join() first.`,
|
|
297
|
+
}));
|
|
298
|
+
}
|
|
299
|
+
const { instance } = contractData;
|
|
367
300
|
yield* Effect.logDebug(`Calling ${action}()...`);
|
|
368
301
|
const deployed = instance;
|
|
369
302
|
const callTx = deployed.callTx;
|
|
@@ -390,10 +323,17 @@ function callEffect(contract, action, ...args) {
|
|
|
390
323
|
};
|
|
391
324
|
});
|
|
392
325
|
}
|
|
393
|
-
function
|
|
326
|
+
function contractStateEffect(contractData) {
|
|
327
|
+
if (contractData.address === undefined) {
|
|
328
|
+
return Effect.fail(new ContractError({
|
|
329
|
+
cause: new Error('Not deployed'),
|
|
330
|
+
message: `Contract not deployed. Call deploy() or join() first.`,
|
|
331
|
+
}));
|
|
332
|
+
}
|
|
333
|
+
const address = contractData.address;
|
|
394
334
|
return Effect.tryPromise({
|
|
395
335
|
try: async () => {
|
|
396
|
-
const {
|
|
336
|
+
const { providers } = contractData;
|
|
397
337
|
const contractState = await providers.publicDataProvider.queryContractState(address);
|
|
398
338
|
if (!contractState) {
|
|
399
339
|
throw new Error(`Contract state not found at ${address}`);
|
|
@@ -406,10 +346,17 @@ function stateEffect(contract) {
|
|
|
406
346
|
}),
|
|
407
347
|
});
|
|
408
348
|
}
|
|
409
|
-
function
|
|
349
|
+
function contractStateAtEffect(contractData, blockHeight) {
|
|
350
|
+
if (contractData.address === undefined) {
|
|
351
|
+
return Effect.fail(new ContractError({
|
|
352
|
+
cause: new Error('Not deployed'),
|
|
353
|
+
message: `Contract not deployed. Call deploy() or join() first.`,
|
|
354
|
+
}));
|
|
355
|
+
}
|
|
356
|
+
const address = contractData.address;
|
|
410
357
|
return Effect.tryPromise({
|
|
411
358
|
try: async () => {
|
|
412
|
-
const {
|
|
359
|
+
const { providers } = contractData;
|
|
413
360
|
const contractState = await providers.publicDataProvider.queryContractState(address, {
|
|
414
361
|
type: 'blockHeight',
|
|
415
362
|
blockHeight,
|
|
@@ -425,223 +372,249 @@ function stateAtEffect(contract, blockHeight) {
|
|
|
425
372
|
}),
|
|
426
373
|
});
|
|
427
374
|
}
|
|
428
|
-
function ledgerStateEffect(
|
|
429
|
-
return
|
|
375
|
+
function ledgerStateEffect(contractData) {
|
|
376
|
+
return contractStateEffect(contractData).pipe(Effect.map((data) => contractData.module.ledger(data)));
|
|
430
377
|
}
|
|
431
|
-
function ledgerStateAtEffect(
|
|
432
|
-
return
|
|
378
|
+
function ledgerStateAtEffect(contractData, blockHeight) {
|
|
379
|
+
return contractStateAtEffect(contractData, blockHeight).pipe(Effect.map((data) => contractData.module.ledger(data)));
|
|
433
380
|
}
|
|
434
381
|
// =============================================================================
|
|
435
|
-
//
|
|
382
|
+
// Handle Factories (Create handles with methods from data)
|
|
436
383
|
// =============================================================================
|
|
437
384
|
/**
|
|
438
|
-
* Contract
|
|
439
|
-
*
|
|
440
|
-
* @
|
|
441
|
-
* @category Contract
|
|
385
|
+
* Create a stateful Contract handle from internal data.
|
|
386
|
+
* The contract maintains mutable state internally for simplicity.
|
|
387
|
+
* @internal
|
|
442
388
|
*/
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
* @category inspection
|
|
490
|
-
*/
|
|
491
|
-
ledgerStateAt: async (contract, blockHeight) => {
|
|
492
|
-
return runEffectWithLogging(ledgerStateAtEffect(contract, blockHeight), contract.logging);
|
|
493
|
-
},
|
|
494
|
-
/**
|
|
495
|
-
* Raw Effect APIs for Contract.
|
|
496
|
-
*
|
|
497
|
-
* @since 0.2.0
|
|
498
|
-
* @category effect
|
|
499
|
-
*/
|
|
500
|
-
effect: {
|
|
501
|
-
call: callEffect,
|
|
502
|
-
state: stateEffect,
|
|
503
|
-
stateAt: stateAtEffect,
|
|
504
|
-
ledgerState: ledgerStateEffect,
|
|
505
|
-
ledgerStateAt: ledgerStateAtEffect,
|
|
506
|
-
},
|
|
507
|
-
};
|
|
389
|
+
function createContractHandle(initialData) {
|
|
390
|
+
// Mutable state - the contract data can change via deploy/join
|
|
391
|
+
let data = initialData;
|
|
392
|
+
const handle = {
|
|
393
|
+
// Computed state property
|
|
394
|
+
get state() {
|
|
395
|
+
return data.address !== undefined ? 'deployed' : 'loaded';
|
|
396
|
+
},
|
|
397
|
+
// Data accessors
|
|
398
|
+
get address() {
|
|
399
|
+
return data.address;
|
|
400
|
+
},
|
|
401
|
+
get module() {
|
|
402
|
+
return data.module;
|
|
403
|
+
},
|
|
404
|
+
get providers() {
|
|
405
|
+
return data.providers;
|
|
406
|
+
},
|
|
407
|
+
// Lifecycle methods
|
|
408
|
+
deploy: async (options) => {
|
|
409
|
+
const newData = await runEffectWithLogging(deployContractEffect(data, options), data.logging);
|
|
410
|
+
data = newData; // Update internal state
|
|
411
|
+
},
|
|
412
|
+
join: async (address, options) => {
|
|
413
|
+
const newData = await runEffectWithLogging(joinContractEffect(data, address, options), data.logging);
|
|
414
|
+
data = newData; // Update internal state
|
|
415
|
+
},
|
|
416
|
+
// Contract methods
|
|
417
|
+
call: (action, ...args) => runEffectWithLogging(callContractEffect(data, action, ...args), data.logging),
|
|
418
|
+
getState: () => runEffectWithLogging(contractStateEffect(data), data.logging),
|
|
419
|
+
getStateAt: (blockHeight) => runEffectWithLogging(contractStateAtEffect(data, blockHeight), data.logging),
|
|
420
|
+
ledgerState: () => runEffectWithLogging(ledgerStateEffect(data), data.logging),
|
|
421
|
+
ledgerStateAt: (blockHeight) => runEffectWithLogging(ledgerStateAtEffect(data, blockHeight), data.logging),
|
|
422
|
+
// Effect API
|
|
423
|
+
effect: {
|
|
424
|
+
deploy: (options) => deployContractEffect(data, options).pipe(Effect.tap((newData) => Effect.sync(() => { data = newData; }))),
|
|
425
|
+
join: (address, options) => joinContractEffect(data, address, options).pipe(Effect.tap((newData) => Effect.sync(() => { data = newData; }))),
|
|
426
|
+
call: (action, ...args) => callContractEffect(data, action, ...args),
|
|
427
|
+
getState: () => contractStateEffect(data),
|
|
428
|
+
getStateAt: (blockHeight) => contractStateAtEffect(data, blockHeight),
|
|
429
|
+
ledgerState: () => ledgerStateEffect(data),
|
|
430
|
+
ledgerStateAt: (blockHeight) => ledgerStateAtEffect(data, blockHeight),
|
|
431
|
+
},
|
|
432
|
+
};
|
|
433
|
+
return handle;
|
|
434
|
+
}
|
|
508
435
|
/**
|
|
509
|
-
*
|
|
436
|
+
* Create a MiddayClient handle from internal data.
|
|
437
|
+
* @internal
|
|
438
|
+
*/
|
|
439
|
+
function createClientHandle(data) {
|
|
440
|
+
return {
|
|
441
|
+
// Data accessors
|
|
442
|
+
networkConfig: data.networkConfig,
|
|
443
|
+
providers: data.providers,
|
|
444
|
+
wallet: data.wallet,
|
|
445
|
+
// Promise API - returns Contract directly (new API)
|
|
446
|
+
loadContract: async (options) => {
|
|
447
|
+
const contractData = await runEffectWithLogging(loadContractEffect(data, options), data.logging);
|
|
448
|
+
return createContractHandle(contractData);
|
|
449
|
+
},
|
|
450
|
+
waitForTx: (txHash) => runEffectWithLogging(waitForTxEffect(data, txHash), data.logging),
|
|
451
|
+
// Effect API
|
|
452
|
+
effect: {
|
|
453
|
+
loadContract: (options) => loadContractEffect(data, options).pipe(Effect.map((contractData) => createContractHandle(contractData))),
|
|
454
|
+
waitForTx: (txHash) => waitForTxEffect(data, txHash),
|
|
455
|
+
},
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
// =============================================================================
|
|
459
|
+
// Module - Public API
|
|
460
|
+
// =============================================================================
|
|
461
|
+
/**
|
|
462
|
+
* Create a Midnight client for interacting with contracts.
|
|
510
463
|
*
|
|
511
464
|
* @example
|
|
512
465
|
* ```typescript
|
|
513
|
-
* const
|
|
514
|
-
*
|
|
515
|
-
*
|
|
516
|
-
*
|
|
466
|
+
* const client = await Midday.Client.create({
|
|
467
|
+
* seed: 'your-64-char-hex-seed',
|
|
468
|
+
* networkConfig: Midday.Config.NETWORKS.local,
|
|
469
|
+
* privateStateProvider,
|
|
517
470
|
* });
|
|
518
471
|
*
|
|
519
|
-
*
|
|
472
|
+
* const contract = await client.loadContract({ path: './contracts/counter' });
|
|
473
|
+
* await contract.deploy();
|
|
474
|
+
* await contract.call('increment');
|
|
520
475
|
* ```
|
|
521
476
|
*
|
|
522
477
|
* @since 0.2.0
|
|
523
|
-
* @category
|
|
478
|
+
* @category constructors
|
|
524
479
|
*/
|
|
525
|
-
export
|
|
480
|
+
export async function create(config) {
|
|
481
|
+
const logging = config.logging ?? true;
|
|
482
|
+
const data = await runEffectWithLogging(createClientDataEffect(config), logging);
|
|
483
|
+
return createClientHandle(data);
|
|
526
484
|
}
|
|
527
485
|
/**
|
|
528
|
-
*
|
|
486
|
+
* Create a Midnight client from a connected wallet (browser).
|
|
529
487
|
*
|
|
530
488
|
* @since 0.2.0
|
|
531
|
-
* @category
|
|
489
|
+
* @category constructors
|
|
532
490
|
*/
|
|
533
|
-
export
|
|
491
|
+
export async function fromWallet(connection, config) {
|
|
492
|
+
const logging = config.logging ?? true;
|
|
493
|
+
const data = await runEffectWithLogging(fromWalletDataEffect(connection, config), logging);
|
|
494
|
+
return createClientHandle(data);
|
|
534
495
|
}
|
|
535
496
|
/**
|
|
536
|
-
*
|
|
497
|
+
* Raw Effect APIs for advanced users who want to compose Effects.
|
|
498
|
+
*
|
|
499
|
+
* @example
|
|
500
|
+
* ```typescript
|
|
501
|
+
* const program = Effect.gen(function* () {
|
|
502
|
+
* const client = yield* Midday.Client.effect.create(config);
|
|
503
|
+
* const contract = yield* client.effect.loadContract({ module });
|
|
504
|
+
* yield* contract.effect.deploy();
|
|
505
|
+
* yield* contract.effect.call('increment');
|
|
506
|
+
* });
|
|
507
|
+
* ```
|
|
537
508
|
*
|
|
538
509
|
* @since 0.2.0
|
|
539
|
-
* @category
|
|
510
|
+
* @category effect
|
|
540
511
|
*/
|
|
541
|
-
export
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
512
|
+
export const effect = {
|
|
513
|
+
/**
|
|
514
|
+
* Create a client (Effect version).
|
|
515
|
+
*/
|
|
516
|
+
create: (config) => createClientDataEffect(config).pipe(Effect.map(createClientHandle)),
|
|
517
|
+
/**
|
|
518
|
+
* Create a client from wallet (Effect version).
|
|
519
|
+
*/
|
|
520
|
+
fromWallet: (connection, config) => fromWalletDataEffect(connection, config).pipe(Effect.map(createClientHandle)),
|
|
521
|
+
};
|
|
546
522
|
/**
|
|
547
|
-
*
|
|
523
|
+
* Load a Compact contract from a directory path.
|
|
548
524
|
*
|
|
549
|
-
*
|
|
550
|
-
*
|
|
525
|
+
* Note: Prefer using `client.loadContract({ path })` which handles this automatically.
|
|
526
|
+
* This function is useful when you need to load the module before creating a client.
|
|
527
|
+
*
|
|
528
|
+
* @param contractPath - Absolute path to the contract directory
|
|
529
|
+
* @param options - Optional loading configuration
|
|
530
|
+
* @returns Promise resolving to the contract module and ZK config provider
|
|
531
|
+
*
|
|
532
|
+
* @example
|
|
533
|
+
* ```typescript
|
|
534
|
+
* // Preferred: let loadContract handle it
|
|
535
|
+
* const contract = await client.loadContract({ path: contractPath });
|
|
536
|
+
*
|
|
537
|
+
* // Alternative: load separately (useful for pre-loading)
|
|
538
|
+
* const { module, zkConfig } = await Midday.Client.loadContractModule(contractPath);
|
|
539
|
+
* const contract = await client.loadContract({ module, zkConfig });
|
|
540
|
+
* ```
|
|
541
|
+
*
|
|
542
|
+
* @since 0.4.0
|
|
543
|
+
* @category loading
|
|
551
544
|
*/
|
|
552
|
-
export
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
545
|
+
export async function loadContractModule(contractPath, options = {}) {
|
|
546
|
+
const { moduleSubdir = 'contract', moduleEntry = 'index.js' } = options;
|
|
547
|
+
// Dynamic imports to avoid bundling Node.js code in browser
|
|
548
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
549
|
+
const { join } = require('path');
|
|
550
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
551
|
+
const { NodeZkConfigProvider } = require('@midnight-ntwrk/midnight-js-node-zk-config-provider');
|
|
552
|
+
const modulePath = join(contractPath, moduleSubdir, moduleEntry);
|
|
553
|
+
const module = (await import(modulePath));
|
|
554
|
+
const zkConfig = new NodeZkConfigProvider(contractPath);
|
|
555
|
+
return { module, zkConfig };
|
|
556
|
+
}
|
|
558
557
|
/**
|
|
559
|
-
*
|
|
558
|
+
* Load a contract from URLs (browser environments).
|
|
560
559
|
*
|
|
561
|
-
* @
|
|
562
|
-
* @
|
|
560
|
+
* @param moduleUrl - URL to the contract module
|
|
561
|
+
* @param zkConfigBaseUrl - Base URL for ZK artifacts
|
|
562
|
+
* @returns Promise resolving to the contract module and ZK config provider
|
|
563
|
+
*
|
|
564
|
+
* @since 0.4.0
|
|
565
|
+
* @category loading
|
|
563
566
|
*/
|
|
564
|
-
export
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
567
|
+
export async function loadContractModuleFromUrl(moduleUrl, zkConfigBaseUrl) {
|
|
568
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
569
|
+
const { HttpZkConfigProvider } = require('./providers/HttpZkConfigProvider.js');
|
|
570
|
+
const module = (await import(/* webpackIgnore: true */ moduleUrl));
|
|
571
|
+
const zkConfig = new HttpZkConfigProvider(zkConfigBaseUrl);
|
|
572
|
+
return { module, zkConfig };
|
|
573
|
+
}
|
|
568
574
|
/**
|
|
569
|
-
*
|
|
575
|
+
* Context.Tag for ClientService dependency injection.
|
|
570
576
|
*
|
|
571
577
|
* @since 0.2.0
|
|
572
|
-
* @category
|
|
578
|
+
* @category service
|
|
573
579
|
*/
|
|
574
|
-
export
|
|
575
|
-
|
|
576
|
-
state: stateEffect,
|
|
577
|
-
stateAt: stateAtEffect,
|
|
578
|
-
ledgerState: ledgerStateEffect,
|
|
579
|
-
ledgerStateAt: ledgerStateAtEffect,
|
|
580
|
-
});
|
|
581
|
-
// =============================================================================
|
|
582
|
-
// Layer Factories
|
|
583
|
-
// =============================================================================
|
|
580
|
+
export class ClientService extends Context.Tag('ClientService')() {
|
|
581
|
+
}
|
|
584
582
|
/**
|
|
585
|
-
*
|
|
586
|
-
*
|
|
587
|
-
* Use this when you want to create clients on-demand within your Effect programs.
|
|
588
|
-
* For pre-initialized clients, use `Client.layer(config)` instead.
|
|
589
|
-
*
|
|
590
|
-
* @example
|
|
591
|
-
* ```typescript
|
|
592
|
-
* import { Effect } from 'effect';
|
|
593
|
-
* import * as Midday from '@no-witness-labs/midday-sdk';
|
|
594
|
-
*
|
|
595
|
-
* const program = Effect.gen(function* () {
|
|
596
|
-
* const clientService = yield* Midday.ClientService;
|
|
597
|
-
* const client = yield* clientService.create(config);
|
|
598
|
-
* return client;
|
|
599
|
-
* });
|
|
600
|
-
*
|
|
601
|
-
* await Effect.runPromise(program.pipe(Effect.provide(Midday.Client.services())));
|
|
602
|
-
* ```
|
|
583
|
+
* Live Layer for ClientService.
|
|
603
584
|
*
|
|
604
|
-
* @since 0.
|
|
585
|
+
* @since 0.2.0
|
|
605
586
|
* @category layer
|
|
606
587
|
*/
|
|
607
|
-
export
|
|
608
|
-
|
|
609
|
-
|
|
588
|
+
export const ClientLive = Layer.succeed(ClientService, {
|
|
589
|
+
create: effect.create,
|
|
590
|
+
fromWallet: effect.fromWallet,
|
|
591
|
+
});
|
|
610
592
|
// =============================================================================
|
|
611
593
|
// Pre-configured Client Layer
|
|
612
594
|
// =============================================================================
|
|
613
595
|
/**
|
|
614
|
-
* Context.Tag for a pre-initialized
|
|
615
|
-
*
|
|
616
|
-
* Use with `Client.layer(config)` for dependency injection of a configured client.
|
|
596
|
+
* Context.Tag for a pre-initialized MiddayClient.
|
|
617
597
|
*
|
|
618
598
|
* @since 0.3.0
|
|
619
599
|
* @category service
|
|
620
600
|
*/
|
|
621
|
-
export class
|
|
601
|
+
export class MiddayClientService extends Context.Tag('MiddayClientService')() {
|
|
622
602
|
}
|
|
623
603
|
/**
|
|
624
|
-
* Create a Layer that provides a pre-initialized
|
|
625
|
-
*
|
|
626
|
-
* This is the recommended way to inject a client into Effect programs
|
|
627
|
-
* when you have a known configuration at startup. Follows the same pattern
|
|
628
|
-
* as `Cluster.layer(config)`.
|
|
604
|
+
* Create a Layer that provides a pre-initialized MiddayClient.
|
|
629
605
|
*
|
|
630
606
|
* @example
|
|
631
607
|
* ```typescript
|
|
632
|
-
* import { Effect } from 'effect';
|
|
633
|
-
* import * as Midday from '@no-witness-labs/midday-sdk';
|
|
634
|
-
*
|
|
635
608
|
* const clientLayer = Midday.Client.layer({
|
|
636
609
|
* seed: 'your-64-char-hex-seed',
|
|
637
610
|
* networkConfig: Midday.Config.NETWORKS.local,
|
|
638
|
-
* zkConfigProvider
|
|
639
|
-
* privateStateProvider
|
|
611
|
+
* zkConfigProvider,
|
|
612
|
+
* privateStateProvider,
|
|
640
613
|
* });
|
|
641
614
|
*
|
|
642
615
|
* const program = Effect.gen(function* () {
|
|
643
|
-
* const client = yield* Midday.
|
|
644
|
-
* const builder = yield*
|
|
616
|
+
* const client = yield* Midday.MiddayClientService;
|
|
617
|
+
* const builder = yield* client.effect.loadContract({ module });
|
|
645
618
|
* return builder;
|
|
646
619
|
* });
|
|
647
620
|
*
|
|
@@ -652,38 +625,24 @@ export class MidnightClientService extends Context.Tag('MidnightClientService')(
|
|
|
652
625
|
* @category layer
|
|
653
626
|
*/
|
|
654
627
|
export function layer(config) {
|
|
655
|
-
return Layer.effect(
|
|
628
|
+
return Layer.effect(MiddayClientService, effect.create(config));
|
|
656
629
|
}
|
|
657
630
|
/**
|
|
658
|
-
* Create a Layer that provides a pre-initialized
|
|
659
|
-
*
|
|
660
|
-
* Use this for browser environments with Lace wallet integration.
|
|
661
|
-
*
|
|
662
|
-
* @example
|
|
663
|
-
* ```typescript
|
|
664
|
-
* import { Effect } from 'effect';
|
|
665
|
-
* import * as Midday from '@no-witness-labs/midday-sdk';
|
|
666
|
-
*
|
|
667
|
-
* // After connecting wallet
|
|
668
|
-
* const connection = await Midday.connectWallet('testnet');
|
|
669
|
-
*
|
|
670
|
-
* const clientLayer = Midday.Client.layerFromWallet(connection, {
|
|
671
|
-
* zkConfigProvider: new Midday.HttpZkConfigProvider('https://cdn.example.com/zk'),
|
|
672
|
-
* privateStateProvider: Midday.indexedDBPrivateStateProvider({ privateStateStoreName: 'my-app' }),
|
|
673
|
-
* });
|
|
674
|
-
*
|
|
675
|
-
* const program = Effect.gen(function* () {
|
|
676
|
-
* const client = yield* Midday.MidnightClientService;
|
|
677
|
-
* // Use client...
|
|
678
|
-
* });
|
|
679
|
-
*
|
|
680
|
-
* await Effect.runPromise(program.pipe(Effect.provide(clientLayer)));
|
|
681
|
-
* ```
|
|
631
|
+
* Create a Layer that provides a pre-initialized MiddayClient from a wallet connection.
|
|
682
632
|
*
|
|
683
633
|
* @since 0.3.0
|
|
684
634
|
* @category layer
|
|
685
635
|
*/
|
|
686
636
|
export function layerFromWallet(connection, config) {
|
|
687
|
-
return Layer.effect(
|
|
637
|
+
return Layer.effect(MiddayClientService, effect.fromWallet(connection, config));
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Create a Layer providing all Client-related services.
|
|
641
|
+
*
|
|
642
|
+
* @since 0.3.0
|
|
643
|
+
* @category layer
|
|
644
|
+
*/
|
|
645
|
+
export function services() {
|
|
646
|
+
return ClientLive;
|
|
688
647
|
}
|
|
689
648
|
//# sourceMappingURL=Client.js.map
|