@no-witness-labs/midday-sdk 0.2.0 → 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.
- package/README.md +145 -78
- package/dist/Client.d.ts +464 -72
- package/dist/Client.d.ts.map +1 -1
- package/dist/Client.js +526 -143
- package/dist/Client.js.map +1 -1
- package/dist/Config.d.ts +83 -1
- package/dist/Config.d.ts.map +1 -1
- package/dist/Config.js +72 -15
- package/dist/Config.js.map +1 -1
- package/dist/Providers.d.ts +99 -9
- package/dist/Providers.d.ts.map +1 -1
- package/dist/Providers.js +142 -39
- package/dist/Providers.js.map +1 -1
- package/dist/Wallet.d.ts +88 -1
- package/dist/Wallet.d.ts.map +1 -1
- package/dist/Wallet.js +162 -51
- package/dist/Wallet.js.map +1 -1
- package/dist/index.d.ts +63 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +68 -4
- package/dist/index.js.map +1 -1
- package/dist/providers/HttpZkConfigProvider.d.ts +229 -0
- package/dist/providers/HttpZkConfigProvider.d.ts.map +1 -0
- package/dist/providers/HttpZkConfigProvider.js +275 -0
- package/dist/providers/HttpZkConfigProvider.js.map +1 -0
- package/dist/providers/IndexedDBPrivateStateProvider.d.ts +270 -0
- package/dist/providers/IndexedDBPrivateStateProvider.d.ts.map +1 -0
- package/dist/providers/IndexedDBPrivateStateProvider.js +513 -0
- package/dist/providers/IndexedDBPrivateStateProvider.js.map +1 -0
- package/dist/providers/errors.d.ts +50 -0
- package/dist/providers/errors.d.ts.map +1 -0
- package/dist/providers/errors.js +32 -0
- package/dist/providers/errors.js.map +1 -0
- package/dist/sdk/Type.d.ts +91 -0
- package/dist/sdk/Type.d.ts.map +1 -0
- package/dist/sdk/Type.js +8 -0
- package/dist/sdk/Type.js.map +1 -0
- package/dist/utils/address.d.ts +56 -0
- package/dist/utils/address.d.ts.map +1 -0
- package/dist/utils/address.js +135 -0
- package/dist/utils/address.js.map +1 -0
- package/dist/utils/coin.d.ts +55 -0
- package/dist/utils/coin.d.ts.map +1 -0
- package/dist/utils/coin.js +84 -0
- package/dist/utils/coin.js.map +1 -0
- package/dist/utils/effect-runtime.d.ts +66 -0
- package/dist/utils/effect-runtime.d.ts.map +1 -0
- package/dist/utils/effect-runtime.js +147 -0
- package/dist/utils/effect-runtime.js.map +1 -0
- package/dist/utils/hex.d.ts +62 -0
- package/dist/utils/hex.d.ts.map +1 -0
- package/dist/utils/hex.js +93 -0
- package/dist/utils/hex.js.map +1 -0
- package/dist/wallet/connector.d.ts +191 -0
- package/dist/wallet/connector.d.ts.map +1 -0
- package/dist/wallet/connector.js +183 -0
- package/dist/wallet/connector.js.map +1 -0
- package/dist/wallet/errors.d.ts +22 -0
- package/dist/wallet/errors.d.ts.map +1 -0
- package/dist/wallet/errors.js +16 -0
- package/dist/wallet/errors.js.map +1 -0
- package/dist/wallet/provider.d.ts +102 -0
- package/dist/wallet/provider.d.ts.map +1 -0
- package/dist/wallet/provider.js +139 -0
- package/dist/wallet/provider.js.map +1 -0
- 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
|
-
*
|
|
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
|
|
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
|
-
//
|
|
39
|
+
// Errors
|
|
20
40
|
// =============================================================================
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
|
62
|
+
return {
|
|
63
|
+
info: () => { },
|
|
64
|
+
warn: () => { },
|
|
65
|
+
error: () => { },
|
|
66
|
+
debug: () => { },
|
|
67
|
+
};
|
|
53
68
|
}
|
|
54
|
-
|
|
55
|
-
|
|
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
|
|
77
|
+
// Client - Internal Effect Implementations
|
|
59
78
|
// =============================================================================
|
|
60
|
-
|
|
61
|
-
*
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
*
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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:
|
|
131
|
-
ledger:
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
//
|
|
196
|
+
// Client - Promise API
|
|
152
197
|
// =============================================================================
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
|
286
|
+
return {
|
|
287
|
+
address,
|
|
288
|
+
instance: deployed,
|
|
289
|
+
module,
|
|
290
|
+
providers,
|
|
291
|
+
logger,
|
|
292
|
+
};
|
|
170
293
|
},
|
|
171
|
-
|
|
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
|
|
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
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
},
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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
|