@parity/product-sdk-contracts 0.6.2 → 0.7.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 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";
@@ -21,7 +21,6 @@ import cdmJson from "./cdm.json";
21
21
 
22
22
  const client = await createChainClient({
23
23
  chains: { assetHub: paseo_asset_hub },
24
- rpcs: { assetHub: ["wss://paseo-asset-hub-next-rpc.polkadot.io"] },
25
24
  });
26
25
 
27
26
  const signerManager = new SignerManager();
@@ -55,7 +54,6 @@ Synchronous factory. Builds a `ContractRuntime` internally that wires the typed
55
54
  | `options.signerManager?` | `SignerManager` | Resolves signer + origin from the logged-in account |
56
55
  | `options.defaultOrigin?` | `SS58String` | Static fallback origin for queries |
57
56
  | `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
57
 
60
58
  ### `manager.getContract(library)`
61
59
 
@@ -75,7 +73,7 @@ await registry.publish.tx("domain", "ipfs://cid", 0);
75
73
  // ^ args typed
76
74
  ```
77
75
 
78
- Throws `ContractNotFoundError` if the library name isn't in the manifest for the active target.
76
+ Throws `ContractNotFoundError` if the library name isn't in the manifest.
79
77
 
80
78
  ### `manager.getAddress(library)`
81
79
 
@@ -85,6 +83,25 @@ Returns the on-chain address. Useful for logging or display.
85
83
 
86
84
  Update `origin`, `signer`, or `signerManager` after construction. Only the fields you pass are updated.
87
85
 
86
+ ### `ContractManager.fromLive(cdmJson, runtime, options?)` / `fromLiveClient(cdmJson, client, descriptor, options?)`
87
+
88
+ **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.
89
+
90
+ ```ts
91
+ const manager = await ContractManager.fromLiveClient(cdmJson, client.raw.assetHub, paseo_asset_hub, {
92
+ signerManager,
93
+ // registryAddress?: HexString — defaults to cdmJson.registry
94
+ // libraries?: string[] — subset to resolve; defaults to every contract
95
+ // registryOrigin?: SS58String — origin for the registry dry-run; defaults to defaultOrigin
96
+ });
97
+ ```
98
+
99
+ 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.
100
+
101
+ ### `withLiveContractAddresses(cdmJson, runtime, options?)`
102
+
103
+ 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`.
104
+
88
105
  ## Signer / origin resolution
89
106
 
90
107
  Order, highest wins:
@@ -185,30 +202,21 @@ Top-level shape:
185
202
 
186
203
  ```jsonc
187
204
  {
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
- },
205
+ "registry": "0xf62c2ece29cd8df2e10040ecfa5a894a5c5d9cb0",
194
206
  "dependencies": {
195
- "<targetHash>": {
196
- "@org/contract-name": "latest"
197
- }
207
+ "@org/contract-name": "latest"
198
208
  },
199
209
  "contracts": {
200
- "<targetHash>": {
201
- "@org/contract-name": {
202
- "version": 6,
203
- "address": "0x4A37B123b0BA2A894cA5953f472264921d44e298",
204
- "abi": [ /* Solidity-compatible ABI entries */ ]
205
- }
210
+ "@org/contract-name": {
211
+ "version": 6,
212
+ "address": "0x4A37B123b0BA2A894cA5953f472264921d44e298",
213
+ "abi": [ /* Solidity-compatible ABI entries */ ]
206
214
  }
207
215
  }
208
216
  }
209
217
  ```
210
218
 
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`.
219
+ `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
220
 
213
221
  A real-world example: [`paritytech/playground-cli/cdm.json`](https://github.com/paritytech/playground-cli/blob/main/cdm.json).
214
222
 
@@ -237,8 +245,9 @@ Without codegen, `getContract()` still works — methods are accessible but unty
237
245
 
238
246
  | Error | When |
239
247
  | --- | --- |
240
- | `ContractNotFoundError` | `getContract(name)` and `name` isn't in the manifest for the active target |
248
+ | `ContractNotFoundError` | `getContract(name)` and `name` isn't in the manifest |
241
249
  | `ContractSignerMissingError` | `.tx()` called with no signer + no signerManager + no defaultSigner |
250
+ | `ContractLiveAddressResolutionError` | `fromLive(...)` / `withLiveContractAddresses(...)` couldn't resolve an address from the live CDM registry (no `registry` set, contract unregistered, or the registry query failed) |
242
251
  | `ContractDryRunFailedError` | `.tx()` pre-flight `ReviveApi.call` reported failure — `dispatchError` carries the chain's encoded error (e.g. `ContractReverted`, `OutOfGas`, `AccountNotMapped`) |
243
252
  | Generic | viem ABI decode failures, RPC errors, weight/storage-limit estimation failures — surface from the underlying PAPI typed API |
244
253
 
@@ -252,10 +261,12 @@ export {
252
261
  createContractFromClient,
253
262
  createContractRuntime,
254
263
  createContractRuntimeFromClient,
264
+ withLiveContractAddresses,
255
265
  ensureContractAccountMapped,
256
266
  ContractError,
257
267
  ContractSignerMissingError,
258
268
  ContractNotFoundError,
269
+ ContractLiveAddressResolutionError,
259
270
  ContractDryRunFailedError,
260
271
  };
261
272
  export type {
@@ -264,8 +275,8 @@ export type {
264
275
  ReviveDryRunResult,
265
276
  ReviveDryRunCall,
266
277
  CdmJson,
267
- CdmJsonTarget,
268
278
  CdmJsonContract,
279
+ CdmJsonDependencyVersion,
269
280
  AbiParam,
270
281
  AbiEntry,
271
282
  Contract,
@@ -280,6 +291,7 @@ export type {
280
291
  ContractDefaults,
281
292
  ContractManagerOptions,
282
293
  ContractOptions,
294
+ LiveContractResolutionOptions,
283
295
  };
284
296
 
285
297
  // `@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-pkS1R9Bk.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-pkS1R9Bk.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-pkS1R9Bk.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
  *
@@ -20,7 +29,6 @@ import '@parity/product-sdk-signer';
20
29
  *
21
30
  * const client = await createChainClient({
22
31
  * chains: { assetHub: paseo_asset_hub },
23
- * rpcs: { assetHub: ["wss://paseo-asset-hub-next-rpc.polkadot.io"] },
24
32
  * });
25
33
  * const runtime = createContractRuntime(client.assetHub);
26
34
  * const manager = new ContractManager(cdmJson, runtime, {
@@ -33,8 +41,7 @@ import '@parity/product-sdk-signer';
33
41
  * ```
34
42
  */
35
43
  declare class ContractManager {
36
- private cdmJson;
37
- private targetHash;
44
+ private contracts;
38
45
  private runtime;
39
46
  private defaults;
40
47
  constructor(cdmJson: CdmJson, runtime: ContractRuntime, options?: ContractManagerOptions);
@@ -54,6 +61,16 @@ declare class ContractManager {
54
61
  * @param options - Optional configuration (signerManager, defaults).
55
62
  */
56
63
  static fromClient<TDescriptor>(cdmJson: CdmJson, client: PolkadotClient, descriptor: TDescriptor, options?: ContractManagerOptions & ContractRuntimeOptions): ContractManager;
64
+ /**
65
+ * Create a manager after strictly resolving installed contract addresses
66
+ * from the live CDM registry. ABIs still come from the installed manifest.
67
+ */
68
+ static fromLive(cdmJson: CdmJson, runtime: ContractRuntime, options?: ContractManagerOptions & LiveContractResolutionOptions): Promise<ContractManager>;
69
+ /**
70
+ * Convenience factory for {@link fromLive} when the caller has a raw
71
+ * `PolkadotClient` and descriptor.
72
+ */
73
+ static fromLiveClient<TDescriptor>(cdmJson: CdmJson, client: PolkadotClient, descriptor: TDescriptor, options?: ContractManagerOptions & ContractRuntimeOptions & LiveContractResolutionOptions): Promise<ContractManager>;
57
74
  private getContractData;
58
75
  /**
59
76
  * Get a typed contract handle.
@@ -117,8 +134,17 @@ declare class ContractSignerMissingError extends ContractError {
117
134
  /** A contract was not found in the cdm.json manifest. */
118
135
  declare class ContractNotFoundError extends ContractError {
119
136
  readonly library: string;
120
- readonly targetHash: string;
121
- constructor(library: string, targetHash: string);
137
+ constructor(library: string);
138
+ }
139
+ /** Live CDM registry address resolution failed. */
140
+ declare class ContractLiveAddressResolutionError extends ContractError {
141
+ readonly library: string | undefined;
142
+ readonly detail: unknown;
143
+ constructor(message: string, options?: {
144
+ library?: string;
145
+ detail?: unknown;
146
+ cause?: unknown;
147
+ });
122
148
  }
123
149
  /**
124
150
  * A pre-flight `ReviveApi.call` dry-run reported failure. Thrown from the `.tx()`
@@ -163,4 +189,4 @@ declare class ContractRevertedError extends ContractError {
163
189
  });
164
190
  }
165
191
 
166
- export { AbiEntry, CdmJson, Contract, ContractDef, ContractDefaults, ContractDryRunFailedError, ContractError, ContractManager, ContractManagerOptions, ContractNotFoundError, ContractOptions, type ContractRevertInfo, ContractRevertedError, ContractRuntime, ContractRuntimeOptions, ContractSignerMissingError, Contracts, type DecodedContractRevert, createContract, createContractFromClient };
192
+ 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