@parity/product-sdk-contracts 0.6.1 → 0.7.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/README.md CHANGED
@@ -10,7 +10,7 @@ pnpm add @parity/product-sdk-contracts
10
10
 
11
11
  ## Quick start (with cdm.json)
12
12
 
13
- The `cdm.json` flow is the primary path. A `cdm.json` manifest in your project root pins each contract to an address + ABI per target chain; `ContractManager.fromClient(cdm, client, descriptor)` resolves them at runtime.
13
+ The `cdm.json` flow is the primary path. A `cdm.json` manifest in your project root pins each installed contract to an address + ABI; `ContractManager.fromClient(cdm, client, descriptor)` resolves them at runtime.
14
14
 
15
15
  ```ts
16
16
  import { createChainClient } from "@parity/product-sdk-chain-client";
@@ -55,7 +55,6 @@ Synchronous factory. Builds a `ContractRuntime` internally that wires the typed
55
55
  | `options.signerManager?` | `SignerManager` | Resolves signer + origin from the logged-in account |
56
56
  | `options.defaultOrigin?` | `SS58String` | Static fallback origin for queries |
57
57
  | `options.defaultSigner?` | `PolkadotSigner` | Static fallback signer for txs |
58
- | `options.targetHash?` | `string` | Pin to a specific target. Defaults to first target in the manifest. |
59
58
 
60
59
  ### `manager.getContract(library)`
61
60
 
@@ -75,7 +74,7 @@ await registry.publish.tx("domain", "ipfs://cid", 0);
75
74
  // ^ args typed
76
75
  ```
77
76
 
78
- Throws `ContractNotFoundError` if the library name isn't in the manifest for the active target.
77
+ Throws `ContractNotFoundError` if the library name isn't in the manifest.
79
78
 
80
79
  ### `manager.getAddress(library)`
81
80
 
@@ -85,6 +84,25 @@ Returns the on-chain address. Useful for logging or display.
85
84
 
86
85
  Update `origin`, `signer`, or `signerManager` after construction. Only the fields you pass are updated.
87
86
 
87
+ ### `ContractManager.fromLive(cdmJson, runtime, options?)` / `fromLiveClient(cdmJson, client, descriptor, options?)`
88
+
89
+ **Async** factories. Like `fromClient`, but before constructing the manager they resolve each installed contract's address from the **live CDM registry** instead of trusting the address baked into `cdm.json`. ABIs still come from the installed snapshot — only addresses are refreshed. Dependencies requested as `"latest"` resolve the registry's latest address; pinned numeric dependencies resolve `getAddressAtVersion(...)` so the live address stays aligned with the installed ABI/version.
90
+
91
+ ```ts
92
+ const manager = await ContractManager.fromLiveClient(cdmJson, client.raw.assetHub, paseo_asset_hub, {
93
+ signerManager,
94
+ // registryAddress?: HexString — defaults to cdmJson.registry
95
+ // libraries?: string[] — subset to resolve; defaults to every contract
96
+ // registryOrigin?: SS58String — origin for the registry dry-run; defaults to defaultOrigin
97
+ });
98
+ ```
99
+
100
+ This path is **strict**: if `cdmJson.registry` (or `registryAddress`) is missing, or a contract isn't registered on-chain, or the registry query fails, the promise rejects with `ContractLiveAddressResolutionError` — it never silently falls back to the snapshot address. Use `fromClient` for snapshot-only behavior.
101
+
102
+ ### `withLiveContractAddresses(cdmJson, runtime, options?)`
103
+
104
+ Standalone helper behind `fromLive`. Returns a **cloned** `cdm.json` whose contract addresses have been replaced with live registry addresses (the input is never mutated). Useful when you want the resolved manifest without immediately building a manager. Takes the same `LiveContractResolutionOptions` as `fromLive`.
105
+
88
106
  ## Signer / origin resolution
89
107
 
90
108
  Order, highest wins:
@@ -185,30 +203,21 @@ Top-level shape:
185
203
 
186
204
  ```jsonc
187
205
  {
188
- "targets": {
189
- "<targetHash>": {
190
- "asset-hub": "wss://paseo-asset-hub-next-rpc.polkadot.io",
191
- "bulletin": "https://paseo-bulletin-next-ipfs.polkadot.io"
192
- }
193
- },
206
+ "registry": "0xf62c2ece29cd8df2e10040ecfa5a894a5c5d9cb0",
194
207
  "dependencies": {
195
- "<targetHash>": {
196
- "@org/contract-name": "latest"
197
- }
208
+ "@org/contract-name": "latest"
198
209
  },
199
210
  "contracts": {
200
- "<targetHash>": {
201
- "@org/contract-name": {
202
- "version": 6,
203
- "address": "0x4A37B123b0BA2A894cA5953f472264921d44e298",
204
- "abi": [ /* Solidity-compatible ABI entries */ ]
205
- }
211
+ "@org/contract-name": {
212
+ "version": 6,
213
+ "address": "0x4A37B123b0BA2A894cA5953f472264921d44e298",
214
+ "abi": [ /* Solidity-compatible ABI entries */ ]
206
215
  }
207
216
  }
208
217
  }
209
218
  ```
210
219
 
211
- `<targetHash>` is a 16-character hex string identifying a chain runtime + Bulletin gateway pairing. A manifest can declare multiple targets; `ContractManager` defaults to the first, or pin one via `options.targetHash`.
220
+ `registry` is used by `ContractManager.fromLive(...)` / `withLiveContractAddresses(...)` to resolve current contract addresses from the live CDM registry. `ContractManager.fromClient(...)` uses the installed `contracts` snapshot directly.
212
221
 
213
222
  A real-world example: [`paritytech/playground-cli/cdm.json`](https://github.com/paritytech/playground-cli/blob/main/cdm.json).
214
223
 
@@ -237,8 +246,9 @@ Without codegen, `getContract()` still works — methods are accessible but unty
237
246
 
238
247
  | Error | When |
239
248
  | --- | --- |
240
- | `ContractNotFoundError` | `getContract(name)` and `name` isn't in the manifest for the active target |
249
+ | `ContractNotFoundError` | `getContract(name)` and `name` isn't in the manifest |
241
250
  | `ContractSignerMissingError` | `.tx()` called with no signer + no signerManager + no defaultSigner |
251
+ | `ContractLiveAddressResolutionError` | `fromLive(...)` / `withLiveContractAddresses(...)` couldn't resolve an address from the live CDM registry (no `registry` set, contract unregistered, or the registry query failed) |
242
252
  | `ContractDryRunFailedError` | `.tx()` pre-flight `ReviveApi.call` reported failure — `dispatchError` carries the chain's encoded error (e.g. `ContractReverted`, `OutOfGas`, `AccountNotMapped`) |
243
253
  | Generic | viem ABI decode failures, RPC errors, weight/storage-limit estimation failures — surface from the underlying PAPI typed API |
244
254
 
@@ -252,10 +262,12 @@ export {
252
262
  createContractFromClient,
253
263
  createContractRuntime,
254
264
  createContractRuntimeFromClient,
265
+ withLiveContractAddresses,
255
266
  ensureContractAccountMapped,
256
267
  ContractError,
257
268
  ContractSignerMissingError,
258
269
  ContractNotFoundError,
270
+ ContractLiveAddressResolutionError,
259
271
  ContractDryRunFailedError,
260
272
  };
261
273
  export type {
@@ -264,8 +276,8 @@ export type {
264
276
  ReviveDryRunResult,
265
277
  ReviveDryRunCall,
266
278
  CdmJson,
267
- CdmJsonTarget,
268
279
  CdmJsonContract,
280
+ CdmJsonDependencyVersion,
269
281
  AbiParam,
270
282
  AbiEntry,
271
283
  Contract,
@@ -280,6 +292,7 @@ export type {
280
292
  ContractDefaults,
281
293
  ContractManagerOptions,
282
294
  ContractOptions,
295
+ LiveContractResolutionOptions,
283
296
  };
284
297
 
285
298
  // `@parity/product-sdk-contracts/codegen` (Node-only — build tooling)
package/dist/codegen.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as AbiEntry } from './types-DzYW54TT.js';
1
+ import { A as AbiEntry } from './types-BJl1dir8.js';
2
2
  import 'polkadot-api';
3
3
  import '@parity/product-sdk-tx';
4
4
  import '@parity/product-sdk-signer';
package/dist/index.d.ts CHANGED
@@ -1,9 +1,18 @@
1
1
  import { PolkadotClient, HexString } from 'polkadot-api';
2
- import { C as CdmJson, a as ContractRuntime, b as ContractManagerOptions, c as ContractDefaults, d as ContractRuntimeOptions, e as Contracts, f as Contract, g as ContractDef, A as AbiEntry, h as ContractOptions } from './types-DzYW54TT.js';
3
- export { i as AbiParam, j as CdmJsonContract, k as CdmJsonTarget, l as ContractDryRunAt, P as PrepareOptions, Q as QueryOptions, m as QueryResult, R as ReviveDryRunCall, n as ReviveDryRunCallOptions, o as ReviveDryRunResult, p as ReviveTypedApi, T as TxOptions, q as createContractRuntime, r as createContractRuntimeFromClient, s as ensureContractAccountMapped } from './types-DzYW54TT.js';
2
+ import { C as CdmJson, a as ContractRuntime, b as ContractManagerOptions, c as ContractDefaults, d as ContractRuntimeOptions, L as LiveContractResolutionOptions, e as Contracts, f as Contract, g as ContractDef, A as AbiEntry, h as ContractOptions } from './types-BJl1dir8.js';
3
+ export { i as AbiParam, j as CdmJsonContract, k as CdmJsonDependencyVersion, l as ContractDryRunAt, P as PrepareOptions, Q as QueryOptions, m as QueryResult, R as ReviveDryRunCall, n as ReviveDryRunCallOptions, o as ReviveDryRunResult, p as ReviveTypedApi, T as TxOptions, q as createContractRuntime, r as createContractRuntimeFromClient, s as ensureContractAccountMapped } from './types-BJl1dir8.js';
4
4
  export { BatchableCall, TxResult } from '@parity/product-sdk-tx';
5
5
  import '@parity/product-sdk-signer';
6
6
 
7
+ /**
8
+ * Return a cloned manifest whose installed contract addresses have been
9
+ * replaced by live addresses from the CDM registry.
10
+ *
11
+ * This is intentionally strict: if a requested library cannot be resolved
12
+ * from the registry, the promise rejects. Use `new ContractManager(...)` or
13
+ * `ContractManager.fromClient(...)` directly for snapshot-only behavior.
14
+ */
15
+ declare function withLiveContractAddresses(cdmJson: CdmJson, runtime: ContractRuntime, options?: LiveContractResolutionOptions): Promise<CdmJson>;
7
16
  /**
8
17
  * Manages typed contract interactions backed by a `cdm.json` manifest.
9
18
  *
@@ -33,8 +42,7 @@ import '@parity/product-sdk-signer';
33
42
  * ```
34
43
  */
35
44
  declare class ContractManager {
36
- private cdmJson;
37
- private targetHash;
45
+ private contracts;
38
46
  private runtime;
39
47
  private defaults;
40
48
  constructor(cdmJson: CdmJson, runtime: ContractRuntime, options?: ContractManagerOptions);
@@ -54,6 +62,16 @@ declare class ContractManager {
54
62
  * @param options - Optional configuration (signerManager, defaults).
55
63
  */
56
64
  static fromClient<TDescriptor>(cdmJson: CdmJson, client: PolkadotClient, descriptor: TDescriptor, options?: ContractManagerOptions & ContractRuntimeOptions): ContractManager;
65
+ /**
66
+ * Create a manager after strictly resolving installed contract addresses
67
+ * from the live CDM registry. ABIs still come from the installed manifest.
68
+ */
69
+ static fromLive(cdmJson: CdmJson, runtime: ContractRuntime, options?: ContractManagerOptions & LiveContractResolutionOptions): Promise<ContractManager>;
70
+ /**
71
+ * Convenience factory for {@link fromLive} when the caller has a raw
72
+ * `PolkadotClient` and descriptor.
73
+ */
74
+ static fromLiveClient<TDescriptor>(cdmJson: CdmJson, client: PolkadotClient, descriptor: TDescriptor, options?: ContractManagerOptions & ContractRuntimeOptions & LiveContractResolutionOptions): Promise<ContractManager>;
57
75
  private getContractData;
58
76
  /**
59
77
  * Get a typed contract handle.
@@ -117,8 +135,17 @@ declare class ContractSignerMissingError extends ContractError {
117
135
  /** A contract was not found in the cdm.json manifest. */
118
136
  declare class ContractNotFoundError extends ContractError {
119
137
  readonly library: string;
120
- readonly targetHash: string;
121
- constructor(library: string, targetHash: string);
138
+ constructor(library: string);
139
+ }
140
+ /** Live CDM registry address resolution failed. */
141
+ declare class ContractLiveAddressResolutionError extends ContractError {
142
+ readonly library: string | undefined;
143
+ readonly detail: unknown;
144
+ constructor(message: string, options?: {
145
+ library?: string;
146
+ detail?: unknown;
147
+ cause?: unknown;
148
+ });
122
149
  }
123
150
  /**
124
151
  * A pre-flight `ReviveApi.call` dry-run reported failure. Thrown from the `.tx()`
@@ -163,4 +190,4 @@ declare class ContractRevertedError extends ContractError {
163
190
  });
164
191
  }
165
192
 
166
- export { AbiEntry, CdmJson, Contract, ContractDef, ContractDefaults, ContractDryRunFailedError, ContractError, ContractManager, ContractManagerOptions, ContractNotFoundError, ContractOptions, type ContractRevertInfo, ContractRevertedError, ContractRuntime, ContractRuntimeOptions, ContractSignerMissingError, Contracts, type DecodedContractRevert, createContract, createContractFromClient };
193
+ export { AbiEntry, CdmJson, Contract, ContractDef, ContractDefaults, ContractDryRunFailedError, ContractError, ContractLiveAddressResolutionError, ContractManager, ContractManagerOptions, ContractNotFoundError, ContractOptions, type ContractRevertInfo, ContractRevertedError, ContractRuntime, ContractRuntimeOptions, ContractSignerMissingError, Contracts, type DecodedContractRevert, LiveContractResolutionOptions, createContract, createContractFromClient, withLiveContractAddresses };
package/dist/index.js CHANGED
@@ -23,12 +23,20 @@ var ContractSignerMissingError = class extends ContractError {
23
23
  };
24
24
  var ContractNotFoundError = class extends ContractError {
25
25
  library;
26
- targetHash;
27
- constructor(library, targetHash) {
28
- super(`Contract "${library}" not found in cdm.json for target ${targetHash}`);
26
+ constructor(library) {
27
+ super(`Contract "${library}" not found in cdm.json`);
29
28
  this.name = "ContractNotFoundError";
30
29
  this.library = library;
31
- this.targetHash = targetHash;
30
+ }
31
+ };
32
+ var ContractLiveAddressResolutionError = class extends ContractError {
33
+ library;
34
+ detail;
35
+ constructor(message, options) {
36
+ super(message, options?.cause !== void 0 ? { cause: options.cause } : void 0);
37
+ this.name = "ContractLiveAddressResolutionError";
38
+ this.library = options?.library;
39
+ this.detail = options?.detail;
32
40
  }
33
41
  };
34
42
  var ContractDryRunFailedError = class extends ContractError {
@@ -351,21 +359,132 @@ async function ensureContractAccountMapped(runtime, address, signer, options) {
351
359
  }
352
360
 
353
361
  // src/manager.ts
362
+ var CDM_REGISTRY_ABI = [
363
+ {
364
+ type: "function",
365
+ name: "getAddress",
366
+ inputs: [{ name: "contract_name", type: "string" }],
367
+ outputs: [
368
+ {
369
+ name: "",
370
+ type: "tuple",
371
+ components: [
372
+ { name: "isSome", type: "bool" },
373
+ { name: "value", type: "address" }
374
+ ]
375
+ }
376
+ ],
377
+ stateMutability: "view"
378
+ },
379
+ {
380
+ type: "function",
381
+ name: "getAddressAtVersion",
382
+ inputs: [
383
+ { name: "contract_name", type: "string" },
384
+ { name: "version", type: "uint32" }
385
+ ],
386
+ outputs: [
387
+ {
388
+ name: "",
389
+ type: "tuple",
390
+ components: [
391
+ { name: "isSome", type: "bool" },
392
+ { name: "value", type: "address" }
393
+ ]
394
+ }
395
+ ],
396
+ stateMutability: "view"
397
+ }
398
+ ];
399
+ function cloneCdmJson(cdmJson) {
400
+ const cloneContractMap = (contracts) => Object.fromEntries(
401
+ Object.entries(contracts).map(([library, contract]) => [library, { ...contract }])
402
+ );
403
+ return {
404
+ ...cdmJson,
405
+ dependencies: { ...cdmJson.dependencies },
406
+ contracts: cdmJson.contracts ? cloneContractMap(cdmJson.contracts) : void 0
407
+ };
408
+ }
409
+ function resolveRegistryAddress(cdmJson, override) {
410
+ if (override) return override;
411
+ if (cdmJson.registry) return cdmJson.registry;
412
+ throw new ContractLiveAddressResolutionError(
413
+ "CDM registry address is required for live contract address resolution. Pass registryAddress or set cdm.json registry."
414
+ );
415
+ }
416
+ function patchContractAddress(cdmJson, library, address) {
417
+ const contract = cdmJson.contracts?.[library];
418
+ if (!contract) {
419
+ throw new ContractNotFoundError(library);
420
+ }
421
+ contract.address = address;
422
+ }
423
+ function resolveLiveVersionSpec(cdmJson, library, contract) {
424
+ const requested = cdmJson.dependencies[library];
425
+ if (typeof requested === "number" && Number.isInteger(requested) && requested >= 0) {
426
+ return requested;
427
+ }
428
+ if (typeof requested === "string") {
429
+ if (requested === "latest") return "latest";
430
+ const parsed = Number(requested);
431
+ if (Number.isInteger(parsed) && parsed >= 0) return parsed;
432
+ }
433
+ return contract.version;
434
+ }
435
+ async function queryLiveAddress(registry, library, version) {
436
+ const result = version === "latest" ? await registry.getAddress.query(library) : await registry.getAddressAtVersion.query(library, version);
437
+ if (!result.success) {
438
+ throw new ContractLiveAddressResolutionError(
439
+ 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`,
440
+ { library, detail: result.value }
441
+ );
442
+ }
443
+ const value = result.value;
444
+ if (!value?.isSome) {
445
+ throw new ContractLiveAddressResolutionError(
446
+ version === "latest" ? `Contract "${library}" is not registered in the CDM registry` : `Contract "${library}" version ${version} is not registered in the CDM registry`,
447
+ { library, detail: result.value }
448
+ );
449
+ }
450
+ return value.value;
451
+ }
452
+ async function withLiveContractAddresses(cdmJson, runtime, options) {
453
+ const contracts = cdmJson.contracts;
454
+ if (!contracts || Object.keys(contracts).length === 0) {
455
+ throw new ContractLiveAddressResolutionError(
456
+ "No installed contracts found in cdm.json for live address resolution."
457
+ );
458
+ }
459
+ const libraries = options?.libraries ?? Object.keys(contracts);
460
+ for (const library of libraries) {
461
+ if (!(library in contracts)) {
462
+ throw new ContractNotFoundError(library);
463
+ }
464
+ }
465
+ const registryAddress = resolveRegistryAddress(cdmJson, options?.registryAddress);
466
+ const registry = createContract(runtime, registryAddress, CDM_REGISTRY_ABI, {
467
+ defaultOrigin: options?.registryOrigin
468
+ });
469
+ const liveAddresses = await Promise.all(
470
+ libraries.map(async (library) => {
471
+ const version = resolveLiveVersionSpec(cdmJson, library, contracts[library]);
472
+ return [library, await queryLiveAddress(registry, library, version)];
473
+ })
474
+ );
475
+ const resolved = cloneCdmJson(cdmJson);
476
+ for (const [library, address] of liveAddresses) {
477
+ patchContractAddress(resolved, library, address);
478
+ }
479
+ return resolved;
480
+ }
354
481
  var ContractManager = class _ContractManager {
355
- cdmJson;
356
- targetHash;
482
+ contracts;
357
483
  runtime;
358
484
  defaults;
359
485
  constructor(cdmJson, runtime, options) {
360
- this.cdmJson = cdmJson;
361
486
  this.runtime = runtime;
362
- if (options?.targetHash) {
363
- this.targetHash = options.targetHash;
364
- } else {
365
- const targets = Object.keys(cdmJson.targets);
366
- if (targets.length === 0) throw new Error("No targets found in cdm.json");
367
- this.targetHash = targets[0];
368
- }
487
+ this.contracts = cdmJson.contracts;
369
488
  this.defaults = {
370
489
  signerManager: options?.signerManager,
371
490
  origin: options?.defaultOrigin,
@@ -399,12 +518,30 @@ var ContractManager = class _ContractManager {
399
518
  options
400
519
  );
401
520
  }
521
+ /**
522
+ * Create a manager after strictly resolving installed contract addresses
523
+ * from the live CDM registry. ABIs still come from the installed manifest.
524
+ */
525
+ static async fromLive(cdmJson, runtime, options) {
526
+ const resolved = await withLiveContractAddresses(cdmJson, runtime, {
527
+ ...options,
528
+ registryOrigin: options?.registryOrigin ?? options?.defaultOrigin
529
+ });
530
+ return new _ContractManager(resolved, runtime, options);
531
+ }
532
+ /**
533
+ * Convenience factory for {@link fromLive} when the caller has a raw
534
+ * `PolkadotClient` and descriptor.
535
+ */
536
+ static async fromLiveClient(cdmJson, client, descriptor, options) {
537
+ const runtime = createContractRuntimeFromClient(client, descriptor, options);
538
+ return _ContractManager.fromLive(cdmJson, runtime, options);
539
+ }
402
540
  getContractData(library) {
403
- const contractsForTarget = this.cdmJson.contracts?.[this.targetHash];
404
- if (!contractsForTarget || !(library in contractsForTarget)) {
405
- throw new ContractNotFoundError(library, this.targetHash);
541
+ if (!this.contracts || !(library in this.contracts)) {
542
+ throw new ContractNotFoundError(library);
406
543
  }
407
- return contractsForTarget[library];
544
+ return this.contracts[library];
408
545
  }
409
546
  getContract(library) {
410
547
  const data = this.getContractData(library);
@@ -443,6 +580,6 @@ function createContractFromClient(client, descriptor, address, abi, options) {
443
580
  );
444
581
  }
445
582
 
446
- export { ContractDryRunFailedError, ContractError, ContractManager, ContractNotFoundError, ContractRevertedError, ContractSignerMissingError, createContract, createContractFromClient, createContractRuntime, createContractRuntimeFromClient, ensureContractAccountMapped };
583
+ export { ContractDryRunFailedError, ContractError, ContractLiveAddressResolutionError, ContractManager, ContractNotFoundError, ContractRevertedError, ContractSignerMissingError, createContract, createContractFromClient, createContractRuntime, createContractRuntimeFromClient, ensureContractAccountMapped, withLiveContractAddresses };
447
584
  //# sourceMappingURL=index.js.map
448
585
  //# sourceMappingURL=index.js.map