@parity/product-sdk-contracts 0.0.0-dev.312.0
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/LICENSE +201 -0
- package/README.md +309 -0
- package/dist/chunk-2UN4YA25.js +53 -0
- package/dist/chunk-2UN4YA25.js.map +1 -0
- package/dist/codegen.d.ts +67 -0
- package/dist/codegen.js +83 -0
- package/dist/codegen.js.map +1 -0
- package/dist/index.d.ts +135 -0
- package/dist/index.js +630 -0
- package/dist/index.js.map +1 -0
- package/dist/pvm.d.ts +54 -0
- package/dist/pvm.js +3 -0
- package/dist/pvm.js.map +1 -0
- package/dist/testing.d.ts +85 -0
- package/dist/testing.js +99 -0
- package/dist/testing.js.map +1 -0
- package/dist/types-Dtu4IsSh.d.ts +552 -0
- package/package.json +57 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,630 @@
|
|
|
1
|
+
import { ok, err } from '@parity/result';
|
|
2
|
+
import { isValidH160, ss58ToH160, isValidSs58 } from '@parity/product-sdk-address';
|
|
3
|
+
import { createLogger } from '@parity/product-sdk-logger';
|
|
4
|
+
import { ensureAccountMapped, submitAndWatch } from '@parity/product-sdk-tx';
|
|
5
|
+
import { ss58Address } from '@polkadot-labs/hdkd-helpers';
|
|
6
|
+
import { encodeFunctionData, bytesToHex, decodeErrorResult, decodeFunctionResult } from 'viem';
|
|
7
|
+
|
|
8
|
+
// src/manager.ts
|
|
9
|
+
var ContractError = class extends Error {
|
|
10
|
+
isSdkError = true;
|
|
11
|
+
source = "contracts";
|
|
12
|
+
constructor(message, options) {
|
|
13
|
+
super(message, options);
|
|
14
|
+
this.name = "ContractError";
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
var ContractSignerMissingError = class extends ContractError {
|
|
18
|
+
constructor() {
|
|
19
|
+
super(
|
|
20
|
+
"No signer available. Pass { signer } in call options, set defaultSigner, or provide a signerManager."
|
|
21
|
+
);
|
|
22
|
+
this.name = "ContractSignerMissingError";
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
var ContractNotFoundError = class extends ContractError {
|
|
26
|
+
library;
|
|
27
|
+
constructor(library) {
|
|
28
|
+
super(`Contract "${library}" not found in cdm.json`);
|
|
29
|
+
this.name = "ContractNotFoundError";
|
|
30
|
+
this.library = library;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
var ContractInvalidOriginError = class extends ContractError {
|
|
34
|
+
/** The rejected origin value. */
|
|
35
|
+
origin;
|
|
36
|
+
constructor(origin) {
|
|
37
|
+
const h160Hint = isValidH160(origin) ? " (looks like an H160 \u2014 convert it with h160ToSs58 from @parity/product-sdk-address first)" : "";
|
|
38
|
+
super(`Contract call origin must be an SS58 address, got "${origin}"${h160Hint}`);
|
|
39
|
+
this.name = "ContractInvalidOriginError";
|
|
40
|
+
this.origin = origin;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
var ContractLiveAddressResolutionError = class extends ContractError {
|
|
44
|
+
library;
|
|
45
|
+
detail;
|
|
46
|
+
constructor(message, options) {
|
|
47
|
+
super(message, options?.cause !== void 0 ? { cause: options.cause } : void 0);
|
|
48
|
+
this.name = "ContractLiveAddressResolutionError";
|
|
49
|
+
this.library = options?.library;
|
|
50
|
+
this.detail = options?.detail;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
var ContractDryRunFailedError = class extends ContractError {
|
|
54
|
+
methodName;
|
|
55
|
+
dispatchError;
|
|
56
|
+
constructor(methodName, dispatchError) {
|
|
57
|
+
super(
|
|
58
|
+
`Dry-run failed for "${methodName}": ${typeof dispatchError === "string" ? dispatchError : JSON.stringify(dispatchError)}. The transaction was not submitted.`
|
|
59
|
+
);
|
|
60
|
+
this.name = "ContractDryRunFailedError";
|
|
61
|
+
this.methodName = methodName;
|
|
62
|
+
this.dispatchError = dispatchError;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
function stringifyArg(arg) {
|
|
66
|
+
if (typeof arg === "bigint") return arg.toString();
|
|
67
|
+
return JSON.stringify(arg, (_, v) => typeof v === "bigint" ? v.toString() : v);
|
|
68
|
+
}
|
|
69
|
+
var ContractRevertedError = class extends ContractError {
|
|
70
|
+
methodName;
|
|
71
|
+
data;
|
|
72
|
+
reason;
|
|
73
|
+
decoded;
|
|
74
|
+
constructor(methodName, data, info) {
|
|
75
|
+
const suffix = info?.reason ?? (info?.decoded ? `${info.decoded.errorName}(${(info.decoded.args ?? []).map(stringifyArg).join(", ")})` : data);
|
|
76
|
+
super(
|
|
77
|
+
`Contract reverted in "${methodName}": ${suffix}. The transaction was not submitted.`
|
|
78
|
+
);
|
|
79
|
+
this.name = "ContractRevertedError";
|
|
80
|
+
this.methodName = methodName;
|
|
81
|
+
this.data = data;
|
|
82
|
+
this.reason = info?.reason;
|
|
83
|
+
this.decoded = info?.decoded;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
var log = createLogger("contracts");
|
|
87
|
+
function buildMethodArgMap(abi) {
|
|
88
|
+
const map = {};
|
|
89
|
+
for (const entry of abi) {
|
|
90
|
+
if (entry.type === "function" && entry.name) {
|
|
91
|
+
map[entry.name] = entry.inputs.map((p) => p.name);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return map;
|
|
95
|
+
}
|
|
96
|
+
function extractOverrides(argNames, args) {
|
|
97
|
+
if (args.length > argNames.length && args.length > 0) {
|
|
98
|
+
const last = args[args.length - 1];
|
|
99
|
+
if (last && typeof last === "object" && !Array.isArray(last)) {
|
|
100
|
+
return { positionalArgs: args.slice(0, -1), overrides: last };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return { positionalArgs: args };
|
|
104
|
+
}
|
|
105
|
+
var REVIVE_PALLET_ACCOUNT = new Uint8Array(32);
|
|
106
|
+
REVIVE_PALLET_ACCOUNT.set(new TextEncoder().encode("modlpy/reviv"));
|
|
107
|
+
var QUERY_FALLBACK_ORIGIN = ss58Address(REVIVE_PALLET_ACCOUNT);
|
|
108
|
+
function resolveOrigin(defaults, override, forQuery) {
|
|
109
|
+
if (override) return override;
|
|
110
|
+
const sourceAddr = defaults.signerManager?.getState().selectedAccount?.address;
|
|
111
|
+
if (sourceAddr) return sourceAddr;
|
|
112
|
+
if (defaults.origin) return defaults.origin;
|
|
113
|
+
if (forQuery) {
|
|
114
|
+
log.warn("No origin configured \u2014 using pallet-revive account fallback for query dry-run");
|
|
115
|
+
return QUERY_FALLBACK_ORIGIN;
|
|
116
|
+
}
|
|
117
|
+
return void 0;
|
|
118
|
+
}
|
|
119
|
+
function resolveSigner(defaults, override) {
|
|
120
|
+
return override ?? defaults.signerManager?.getSigner() ?? defaults.signer;
|
|
121
|
+
}
|
|
122
|
+
function validateOrigin(origin) {
|
|
123
|
+
return isValidSs58(origin) ? null : new ContractInvalidOriginError(origin);
|
|
124
|
+
}
|
|
125
|
+
function normalizeContractAddress(address) {
|
|
126
|
+
const hex = address.startsWith("0x") ? address.slice(2) : address;
|
|
127
|
+
if (hex.length !== 40) {
|
|
128
|
+
throw new Error(`Expected 20-byte H160 contract address, got ${hex.length / 2} bytes`);
|
|
129
|
+
}
|
|
130
|
+
return `0x${hex.toLowerCase()}`;
|
|
131
|
+
}
|
|
132
|
+
function hexToBytes(hex) {
|
|
133
|
+
const stripped = hex.slice(2);
|
|
134
|
+
const out = new Uint8Array(stripped.length / 2);
|
|
135
|
+
for (let i = 0; i < out.length; i++) {
|
|
136
|
+
out[i] = Number.parseInt(stripped.slice(i * 2, i * 2 + 2), 16);
|
|
137
|
+
}
|
|
138
|
+
return out;
|
|
139
|
+
}
|
|
140
|
+
var REVERT_FLAG = 1;
|
|
141
|
+
var PANIC_REASONS = {
|
|
142
|
+
"0x01": "assertion failed",
|
|
143
|
+
"0x11": "arithmetic overflow",
|
|
144
|
+
"0x12": "division by zero",
|
|
145
|
+
"0x21": "invalid enum value",
|
|
146
|
+
"0x22": "improperly encoded storage byte array",
|
|
147
|
+
"0x31": "pop on empty array",
|
|
148
|
+
"0x32": "array index out of bounds",
|
|
149
|
+
"0x41": "memory allocation overflow",
|
|
150
|
+
"0x51": "call to uninitialized internal function"
|
|
151
|
+
};
|
|
152
|
+
function decodeRevert(abi, data) {
|
|
153
|
+
const hex = bytesToHex(data);
|
|
154
|
+
const info = {
|
|
155
|
+
type: "ContractRevertedWithPayload",
|
|
156
|
+
data: hex
|
|
157
|
+
};
|
|
158
|
+
if (data.byteLength === 0) return info;
|
|
159
|
+
try {
|
|
160
|
+
const decoded = decodeErrorResult({
|
|
161
|
+
abi,
|
|
162
|
+
data: hex
|
|
163
|
+
});
|
|
164
|
+
info.decoded = {
|
|
165
|
+
errorName: decoded.errorName,
|
|
166
|
+
args: decoded.args
|
|
167
|
+
};
|
|
168
|
+
if (decoded.errorName === "Error" && typeof decoded.args?.[0] === "string") {
|
|
169
|
+
info.reason = decoded.args[0];
|
|
170
|
+
} else if (decoded.errorName === "Panic" && typeof decoded.args?.[0] === "bigint") {
|
|
171
|
+
const code = `0x${decoded.args[0].toString(16).padStart(2, "0")}`;
|
|
172
|
+
info.reason = PANIC_REASONS[code] ? `Panic: ${PANIC_REASONS[code]}` : `Panic(${code})`;
|
|
173
|
+
}
|
|
174
|
+
return info;
|
|
175
|
+
} catch {
|
|
176
|
+
try {
|
|
177
|
+
info.reason = new TextDecoder("utf-8", { fatal: true }).decode(data);
|
|
178
|
+
} catch {
|
|
179
|
+
}
|
|
180
|
+
return info;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function encodeCalldata(abi, methodName, args) {
|
|
184
|
+
return encodeFunctionData({
|
|
185
|
+
abi,
|
|
186
|
+
functionName: methodName,
|
|
187
|
+
args
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
function decodeReturn(abi, methodName, returnData) {
|
|
191
|
+
if (returnData.byteLength === 0) return void 0;
|
|
192
|
+
const decoded = decodeFunctionResult({
|
|
193
|
+
abi,
|
|
194
|
+
functionName: methodName,
|
|
195
|
+
data: bytesToHex(returnData)
|
|
196
|
+
});
|
|
197
|
+
const entry = abi.find((e) => e.type === "function" && e.name === methodName);
|
|
198
|
+
const outputs = entry?.outputs ?? [];
|
|
199
|
+
if (outputs.length <= 1 || !Array.isArray(decoded)) return decoded;
|
|
200
|
+
const obj = {};
|
|
201
|
+
for (let i = 0; i < outputs.length; i++) {
|
|
202
|
+
obj[outputs[i].name || `_${i}`] = decoded[i];
|
|
203
|
+
}
|
|
204
|
+
return obj;
|
|
205
|
+
}
|
|
206
|
+
async function buildReviveCall(runtime, dest, abi, methodName, positionalArgs, origin, overrides) {
|
|
207
|
+
const value = overrides?.value ?? 0n;
|
|
208
|
+
const calldata = hexToBytes(encodeCalldata(abi, methodName, positionalArgs));
|
|
209
|
+
let weightLimit = overrides?.gasLimit;
|
|
210
|
+
let storageDepositLimit = overrides?.storageDepositLimit;
|
|
211
|
+
if (weightLimit === void 0 || storageDepositLimit === void 0) {
|
|
212
|
+
const dryRun = await runtime.dryRunCall(
|
|
213
|
+
origin,
|
|
214
|
+
dest,
|
|
215
|
+
value,
|
|
216
|
+
void 0,
|
|
217
|
+
void 0,
|
|
218
|
+
calldata,
|
|
219
|
+
overrides?.at !== void 0 ? { at: overrides.at } : void 0
|
|
220
|
+
);
|
|
221
|
+
if (!dryRun.result.success) {
|
|
222
|
+
return err(new ContractDryRunFailedError(methodName, dryRun.result.value));
|
|
223
|
+
}
|
|
224
|
+
if ((dryRun.result.value.flags & REVERT_FLAG) !== 0) {
|
|
225
|
+
const { data, reason, decoded } = decodeRevert(abi, dryRun.result.value.data);
|
|
226
|
+
log.debug("Contract reverted", { methodName, reason, errorName: decoded?.errorName });
|
|
227
|
+
return err(new ContractRevertedError(methodName, data, { reason, decoded }));
|
|
228
|
+
}
|
|
229
|
+
weightLimit = weightLimit ?? dryRun.weight_required;
|
|
230
|
+
if (storageDepositLimit === void 0) {
|
|
231
|
+
storageDepositLimit = dryRun.storage_deposit.type === "Charge" ? dryRun.storage_deposit.value : 0n;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return ok(
|
|
235
|
+
runtime.api.tx.Revive.call({
|
|
236
|
+
dest,
|
|
237
|
+
value,
|
|
238
|
+
weight_limit: weightLimit,
|
|
239
|
+
storage_deposit_limit: storageDepositLimit,
|
|
240
|
+
data: calldata
|
|
241
|
+
})
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
function wrapContract(runtime, address, abi, defaults) {
|
|
245
|
+
const methodArgs = buildMethodArgMap(abi);
|
|
246
|
+
const dest = normalizeContractAddress(address);
|
|
247
|
+
return new Proxy({}, {
|
|
248
|
+
get(_, methodName) {
|
|
249
|
+
if (typeof methodName !== "string") return void 0;
|
|
250
|
+
const argNames = methodArgs[methodName];
|
|
251
|
+
if (!argNames) return void 0;
|
|
252
|
+
return {
|
|
253
|
+
query: async (...args) => {
|
|
254
|
+
const { positionalArgs, overrides } = extractOverrides(
|
|
255
|
+
argNames,
|
|
256
|
+
args
|
|
257
|
+
);
|
|
258
|
+
const origin = resolveOrigin(defaults, overrides?.origin, true);
|
|
259
|
+
const originError = validateOrigin(origin);
|
|
260
|
+
if (originError) throw originError;
|
|
261
|
+
const value = overrides?.value ?? 0n;
|
|
262
|
+
const calldata = hexToBytes(encodeCalldata(abi, methodName, positionalArgs));
|
|
263
|
+
const dryRun = await runtime.dryRunCall(
|
|
264
|
+
origin,
|
|
265
|
+
dest,
|
|
266
|
+
value,
|
|
267
|
+
void 0,
|
|
268
|
+
void 0,
|
|
269
|
+
calldata,
|
|
270
|
+
overrides?.at !== void 0 ? { at: overrides.at } : void 0
|
|
271
|
+
);
|
|
272
|
+
if (!dryRun.result.success) {
|
|
273
|
+
return {
|
|
274
|
+
success: false,
|
|
275
|
+
value: dryRun.result.value,
|
|
276
|
+
gasRequired: dryRun.weight_required
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
if ((dryRun.result.value.flags & REVERT_FLAG) !== 0) {
|
|
280
|
+
const info = decodeRevert(abi, dryRun.result.value.data);
|
|
281
|
+
log.debug("Contract reverted", {
|
|
282
|
+
methodName,
|
|
283
|
+
reason: info.reason,
|
|
284
|
+
errorName: info.decoded?.errorName
|
|
285
|
+
});
|
|
286
|
+
return {
|
|
287
|
+
success: false,
|
|
288
|
+
value: info,
|
|
289
|
+
gasRequired: dryRun.weight_required
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
const decoded = decodeReturn(abi, methodName, dryRun.result.value.data);
|
|
293
|
+
return {
|
|
294
|
+
success: true,
|
|
295
|
+
value: decoded,
|
|
296
|
+
gasRequired: dryRun.weight_required
|
|
297
|
+
};
|
|
298
|
+
},
|
|
299
|
+
tx: async (...args) => {
|
|
300
|
+
const { positionalArgs, overrides } = extractOverrides(
|
|
301
|
+
argNames,
|
|
302
|
+
args
|
|
303
|
+
);
|
|
304
|
+
const signer = resolveSigner(defaults, overrides?.signer);
|
|
305
|
+
if (!signer) {
|
|
306
|
+
return err(new ContractSignerMissingError());
|
|
307
|
+
}
|
|
308
|
+
const origin = resolveOrigin(defaults, overrides?.origin) ?? ss58Address(signer.publicKey);
|
|
309
|
+
const originError = validateOrigin(origin);
|
|
310
|
+
if (originError) return err(originError);
|
|
311
|
+
const built = await buildReviveCall(
|
|
312
|
+
runtime,
|
|
313
|
+
dest,
|
|
314
|
+
abi,
|
|
315
|
+
methodName,
|
|
316
|
+
positionalArgs,
|
|
317
|
+
origin,
|
|
318
|
+
overrides
|
|
319
|
+
);
|
|
320
|
+
if (!built.ok) return built;
|
|
321
|
+
return submitAndWatch(built.value, signer, {
|
|
322
|
+
waitFor: overrides?.waitFor,
|
|
323
|
+
timeoutMs: overrides?.timeoutMs,
|
|
324
|
+
mortalityPeriod: overrides?.mortalityPeriod,
|
|
325
|
+
onStatus: overrides?.onStatus
|
|
326
|
+
});
|
|
327
|
+
},
|
|
328
|
+
prepare: async (...args) => {
|
|
329
|
+
const { positionalArgs, overrides } = extractOverrides(
|
|
330
|
+
argNames,
|
|
331
|
+
args
|
|
332
|
+
);
|
|
333
|
+
const origin = resolveOrigin(defaults, overrides?.origin, true);
|
|
334
|
+
const originError = validateOrigin(origin);
|
|
335
|
+
if (originError) return err(originError);
|
|
336
|
+
return buildReviveCall(
|
|
337
|
+
runtime,
|
|
338
|
+
dest,
|
|
339
|
+
abi,
|
|
340
|
+
methodName,
|
|
341
|
+
positionalArgs,
|
|
342
|
+
origin,
|
|
343
|
+
overrides
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
function createContractRuntime(api, options) {
|
|
351
|
+
const defaultAt = options?.at ?? "best";
|
|
352
|
+
return {
|
|
353
|
+
api,
|
|
354
|
+
dryRunCall: (origin, dest, value, gas, deposit, data, callOpts) => api.apis.ReviveApi.call(origin, dest, value, gas, deposit, data, {
|
|
355
|
+
at: callOpts?.at ?? defaultAt
|
|
356
|
+
})
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
function createContractRuntimeFromClient(client, descriptor, options) {
|
|
360
|
+
const defaultAt = options?.at ?? "best";
|
|
361
|
+
const typed = client.getTypedApi(
|
|
362
|
+
descriptor
|
|
363
|
+
);
|
|
364
|
+
const unsafe = client.getUnsafeApi();
|
|
365
|
+
return {
|
|
366
|
+
api: typed,
|
|
367
|
+
dryRunCall: (origin, dest, value, gas, deposit, data, callOpts) => unsafe.apis.ReviveApi.call(origin, dest, value, gas, deposit, data, {
|
|
368
|
+
at: callOpts?.at ?? defaultAt
|
|
369
|
+
})
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
async function ensureContractAccountMapped(runtime, address, signer, options) {
|
|
373
|
+
const checker = {
|
|
374
|
+
addressIsMapped: async (addr) => {
|
|
375
|
+
const mapped = await isContractAccountMapped(runtime, addr);
|
|
376
|
+
if (!mapped.ok) throw mapped.error;
|
|
377
|
+
return mapped.value;
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
return ensureAccountMapped(address, signer, checker, runtime.api, options);
|
|
381
|
+
}
|
|
382
|
+
async function isContractAccountMapped(runtime, address) {
|
|
383
|
+
try {
|
|
384
|
+
const h160 = ss58ToH160(address);
|
|
385
|
+
const mapped = await runtime.api.query.Revive.OriginalAccount.getValue(h160) !== void 0;
|
|
386
|
+
return ok(mapped);
|
|
387
|
+
} catch (cause) {
|
|
388
|
+
return err(
|
|
389
|
+
new ContractError(`Failed to check pallet-revive mapping for ${address}`, { cause })
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// src/manager.ts
|
|
395
|
+
var CDM_REGISTRY_ABI = [
|
|
396
|
+
{
|
|
397
|
+
type: "function",
|
|
398
|
+
name: "getAddress",
|
|
399
|
+
inputs: [{ name: "contract_name", type: "string" }],
|
|
400
|
+
outputs: [
|
|
401
|
+
{
|
|
402
|
+
name: "",
|
|
403
|
+
type: "tuple",
|
|
404
|
+
components: [
|
|
405
|
+
{ name: "isSome", type: "bool" },
|
|
406
|
+
{ name: "value", type: "address" }
|
|
407
|
+
]
|
|
408
|
+
}
|
|
409
|
+
],
|
|
410
|
+
stateMutability: "view"
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
type: "function",
|
|
414
|
+
name: "getAddressAtVersion",
|
|
415
|
+
inputs: [
|
|
416
|
+
{ name: "contract_name", type: "string" },
|
|
417
|
+
{ name: "version", type: "uint32" }
|
|
418
|
+
],
|
|
419
|
+
outputs: [
|
|
420
|
+
{
|
|
421
|
+
name: "",
|
|
422
|
+
type: "tuple",
|
|
423
|
+
components: [
|
|
424
|
+
{ name: "isSome", type: "bool" },
|
|
425
|
+
{ name: "value", type: "address" }
|
|
426
|
+
]
|
|
427
|
+
}
|
|
428
|
+
],
|
|
429
|
+
stateMutability: "view"
|
|
430
|
+
}
|
|
431
|
+
];
|
|
432
|
+
function cloneCdmJson(cdmJson) {
|
|
433
|
+
const cloneContractMap = (contracts) => Object.fromEntries(
|
|
434
|
+
Object.entries(contracts).map(([library, contract]) => [library, { ...contract }])
|
|
435
|
+
);
|
|
436
|
+
return {
|
|
437
|
+
...cdmJson,
|
|
438
|
+
dependencies: { ...cdmJson.dependencies },
|
|
439
|
+
contracts: cdmJson.contracts ? cloneContractMap(cdmJson.contracts) : void 0
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
function resolveRegistryAddress(cdmJson, override) {
|
|
443
|
+
if (override) return override;
|
|
444
|
+
if (cdmJson.registry) return cdmJson.registry;
|
|
445
|
+
throw new ContractLiveAddressResolutionError(
|
|
446
|
+
"CDM registry address is required for live contract address resolution. Pass registryAddress or set cdm.json registry."
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
function patchContractAddress(cdmJson, library, address) {
|
|
450
|
+
const contract = cdmJson.contracts?.[library];
|
|
451
|
+
if (!contract) {
|
|
452
|
+
throw new ContractNotFoundError(library);
|
|
453
|
+
}
|
|
454
|
+
contract.address = address;
|
|
455
|
+
}
|
|
456
|
+
function resolveLiveVersionSpec(cdmJson, library, contract) {
|
|
457
|
+
const requested = cdmJson.dependencies[library];
|
|
458
|
+
if (typeof requested === "number" && Number.isInteger(requested) && requested >= 0) {
|
|
459
|
+
return requested;
|
|
460
|
+
}
|
|
461
|
+
if (typeof requested === "string") {
|
|
462
|
+
if (requested === "latest") return "latest";
|
|
463
|
+
const parsed = Number(requested);
|
|
464
|
+
if (Number.isInteger(parsed) && parsed >= 0) return parsed;
|
|
465
|
+
}
|
|
466
|
+
return contract.version;
|
|
467
|
+
}
|
|
468
|
+
async function queryLiveAddress(registry, library, version) {
|
|
469
|
+
const result = version === "latest" ? await registry.getAddress.query(library) : await registry.getAddressAtVersion.query(library, version);
|
|
470
|
+
if (!result.success) {
|
|
471
|
+
throw new ContractLiveAddressResolutionError(
|
|
472
|
+
version === "latest" ? `Failed to resolve live address for "${library}" from the CDM registry` : `Failed to resolve live address for "${library}" version ${version} from the CDM registry`,
|
|
473
|
+
{ library, detail: result.value }
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
const value = result.value;
|
|
477
|
+
if (!value?.isSome) {
|
|
478
|
+
throw new ContractLiveAddressResolutionError(
|
|
479
|
+
version === "latest" ? `Contract "${library}" is not registered in the CDM registry` : `Contract "${library}" version ${version} is not registered in the CDM registry`,
|
|
480
|
+
{ library, detail: result.value }
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
return value.value;
|
|
484
|
+
}
|
|
485
|
+
async function withLiveContractAddresses(cdmJson, runtime, options) {
|
|
486
|
+
try {
|
|
487
|
+
const contracts = cdmJson.contracts;
|
|
488
|
+
if (!contracts || Object.keys(contracts).length === 0) {
|
|
489
|
+
throw new ContractLiveAddressResolutionError(
|
|
490
|
+
"No installed contracts found in cdm.json for live address resolution."
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
const libraries = options?.libraries ?? Object.keys(contracts);
|
|
494
|
+
for (const library of libraries) {
|
|
495
|
+
if (!(library in contracts)) {
|
|
496
|
+
throw new ContractNotFoundError(library);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
const registryAddress = resolveRegistryAddress(cdmJson, options?.registryAddress);
|
|
500
|
+
const registry = createContract(runtime, registryAddress, CDM_REGISTRY_ABI, {
|
|
501
|
+
defaultOrigin: options?.registryOrigin
|
|
502
|
+
});
|
|
503
|
+
const liveAddresses = await Promise.all(
|
|
504
|
+
libraries.map(async (library) => {
|
|
505
|
+
const version = resolveLiveVersionSpec(cdmJson, library, contracts[library]);
|
|
506
|
+
return [library, await queryLiveAddress(registry, library, version)];
|
|
507
|
+
})
|
|
508
|
+
);
|
|
509
|
+
const resolved = cloneCdmJson(cdmJson);
|
|
510
|
+
for (const [library, address] of liveAddresses) {
|
|
511
|
+
patchContractAddress(resolved, library, address);
|
|
512
|
+
}
|
|
513
|
+
return ok(resolved);
|
|
514
|
+
} catch (cause) {
|
|
515
|
+
if (cause instanceof ContractError) return err(cause);
|
|
516
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
517
|
+
return err(
|
|
518
|
+
new ContractLiveAddressResolutionError(
|
|
519
|
+
`Live contract address resolution failed: ${message}`,
|
|
520
|
+
{ cause }
|
|
521
|
+
)
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
var ContractManager = class _ContractManager {
|
|
526
|
+
contracts;
|
|
527
|
+
runtime;
|
|
528
|
+
defaults;
|
|
529
|
+
constructor(cdmJson, runtime, options) {
|
|
530
|
+
this.runtime = runtime;
|
|
531
|
+
this.contracts = cdmJson.contracts;
|
|
532
|
+
this.defaults = {
|
|
533
|
+
signerManager: options?.signerManager,
|
|
534
|
+
origin: options?.defaultOrigin,
|
|
535
|
+
signer: options?.defaultSigner
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
/** Update the default origin, signer, or signerManager used by all contract handles. */
|
|
539
|
+
setDefaults(defaults) {
|
|
540
|
+
if (defaults.signerManager !== void 0)
|
|
541
|
+
this.defaults.signerManager = defaults.signerManager;
|
|
542
|
+
if (defaults.origin !== void 0) this.defaults.origin = defaults.origin;
|
|
543
|
+
if (defaults.signer !== void 0) this.defaults.signer = defaults.signer;
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Create a `ContractManager` from a raw `PolkadotClient`.
|
|
547
|
+
*
|
|
548
|
+
* Convenience factory: builds a `ContractRuntime` internally from the
|
|
549
|
+
* client's typed API. Requires that the chain's typed API exposes the
|
|
550
|
+
* `Revive` pallet and `ReviveApi` runtime API (Asset Hub Paseo /
|
|
551
|
+
* Polkadot / Kusama).
|
|
552
|
+
*
|
|
553
|
+
* @param cdmJson - The CDM manifest.
|
|
554
|
+
* @param client - A `PolkadotClient` for the chain where contracts are deployed.
|
|
555
|
+
* @param descriptor - The chain descriptor used to derive the typed API.
|
|
556
|
+
* @param options - Optional configuration (signerManager, defaults).
|
|
557
|
+
*/
|
|
558
|
+
static fromClient(cdmJson, client, descriptor, options) {
|
|
559
|
+
return new _ContractManager(
|
|
560
|
+
cdmJson,
|
|
561
|
+
createContractRuntimeFromClient(client, descriptor, options),
|
|
562
|
+
options
|
|
563
|
+
);
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Create a manager after strictly resolving installed contract addresses
|
|
567
|
+
* from the live CDM registry. ABIs still come from the installed manifest.
|
|
568
|
+
*/
|
|
569
|
+
static async fromLive(cdmJson, runtime, options) {
|
|
570
|
+
const resolved = await withLiveContractAddresses(cdmJson, runtime, {
|
|
571
|
+
...options,
|
|
572
|
+
registryOrigin: options?.registryOrigin ?? options?.defaultOrigin
|
|
573
|
+
});
|
|
574
|
+
if (!resolved.ok) return resolved;
|
|
575
|
+
return ok(new _ContractManager(resolved.value, runtime, options));
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Convenience factory for {@link fromLive} when the caller has a raw
|
|
579
|
+
* `PolkadotClient` and descriptor.
|
|
580
|
+
*/
|
|
581
|
+
static async fromLiveClient(cdmJson, client, descriptor, options) {
|
|
582
|
+
const runtime = createContractRuntimeFromClient(client, descriptor, options);
|
|
583
|
+
return _ContractManager.fromLive(cdmJson, runtime, options);
|
|
584
|
+
}
|
|
585
|
+
getContractData(library) {
|
|
586
|
+
if (!this.contracts || !(library in this.contracts)) {
|
|
587
|
+
throw new ContractNotFoundError(library);
|
|
588
|
+
}
|
|
589
|
+
return this.contracts[library];
|
|
590
|
+
}
|
|
591
|
+
getContract(library) {
|
|
592
|
+
const data = this.getContractData(library);
|
|
593
|
+
return wrapContract(this.runtime, data.address, data.abi, this.defaults);
|
|
594
|
+
}
|
|
595
|
+
/** Get the on-chain address of an installed contract. */
|
|
596
|
+
getAddress(library) {
|
|
597
|
+
return this.getContractData(library).address;
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Get the underlying {@link ContractRuntime} backing this manager.
|
|
601
|
+
*
|
|
602
|
+
* Useful when a consumer needs to call helpers that take a runtime
|
|
603
|
+
* directly — most commonly {@link ensureContractAccountMapped} at app
|
|
604
|
+
* boot. Avoids the alternative of building a second runtime against the
|
|
605
|
+
* same client and descriptor.
|
|
606
|
+
*/
|
|
607
|
+
getRuntime() {
|
|
608
|
+
return this.runtime;
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
function createContract(runtime, address, abi, options) {
|
|
612
|
+
const defaults = {
|
|
613
|
+
signerManager: options?.signerManager,
|
|
614
|
+
origin: options?.defaultOrigin,
|
|
615
|
+
signer: options?.defaultSigner
|
|
616
|
+
};
|
|
617
|
+
return wrapContract(runtime, address, abi, defaults);
|
|
618
|
+
}
|
|
619
|
+
function createContractFromClient(client, descriptor, address, abi, options) {
|
|
620
|
+
return createContract(
|
|
621
|
+
createContractRuntimeFromClient(client, descriptor, options),
|
|
622
|
+
address,
|
|
623
|
+
abi,
|
|
624
|
+
options
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
export { ContractDryRunFailedError, ContractError, ContractInvalidOriginError, ContractLiveAddressResolutionError, ContractManager, ContractNotFoundError, ContractRevertedError, ContractSignerMissingError, QUERY_FALLBACK_ORIGIN, createContract, createContractFromClient, createContractRuntime, createContractRuntimeFromClient, ensureContractAccountMapped, isContractAccountMapped, withLiveContractAddresses };
|
|
629
|
+
//# sourceMappingURL=index.js.map
|
|
630
|
+
//# sourceMappingURL=index.js.map
|