@parity/product-sdk-contracts 0.5.1 → 0.6.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
@@ -92,7 +92,7 @@ Order, highest wins:
92
92
  1. Explicit `{ signer }` / `{ origin }` in the call options
93
93
  2. `signerManager`'s currently selected account
94
94
  3. Static `defaultSigner` / `defaultOrigin`
95
- 4. (Queries only) Dev fallback (Alice) for dry-run gas estimation
95
+ 4. (Queries only) pallet-revive account fallback for the dry-run
96
96
 
97
97
  Throws `ContractSignerMissingError` from `.tx()` if no signer is available. `.query()` and `.prepare()` never need a signer.
98
98
 
@@ -113,6 +113,31 @@ Without this, every fresh-account `.tx()` fails the pre-flight dry-run with `Acc
113
113
 
114
114
  Every `.tx()` runs a `ReviveApi.call` dry-run first to size `weight_limit` / `storage_deposit_limit` and to fail fast on revert, OOG, or `AccountNotMapped` before any signing happens. Throws `ContractDryRunFailedError` (with the chain's `dispatchError`) when the dry-run reports failure — caller pays no gas on a tx the chain already rejected. Pass both `gasLimit` and `storageDepositLimit` overrides on `TxOptions` to skip the dry-run entirely.
115
115
 
116
+ ## Query block selection
117
+
118
+ `.query()` and the `.tx()` / `.prepare()` sizing dry-runs target best-block by default, matching `submitAndWatch`'s default resolution. This keeps reads consistent with the state a freshly-submitted transaction observes.
119
+
120
+ Override per call via `QueryOptions.at`, `TxOptions.at`, or `PrepareOptions.at` — each accepts `"best"`, `"finalized"`, or a block hash:
121
+
122
+ ```ts
123
+ await counter.getCount.query(); // best-block (default)
124
+ await counter.getCount.query({ at: "finalized" }); // canonical, lagged
125
+ await counter.getCount.query({ at: blockHash }); // pin to a historical block
126
+
127
+ await counter.increment.tx({ at: "finalized" }); // size the dry-run against finalized
128
+ await counter.increment.prepare({ at: blockHash }); // pin the batched call's sizing dry-run
129
+ ```
130
+
131
+ `.tx({ at })` / `.prepare({ at })` is a no-op when both `gasLimit` and `storageDepositLimit` overrides are supplied — the sizing dry-run is skipped entirely in that case.
132
+
133
+ Change the runtime default by passing `{ at }` to the factory:
134
+
135
+ ```ts
136
+ const runtime = createContractRuntimeFromClient(client.raw.assetHub, paseo_asset_hub, {
137
+ at: "finalized", // read finalized state by default
138
+ });
139
+ ```
140
+
116
141
  ## Batching with `.prepare()`
117
142
 
118
143
  Use `.prepare()` to build `BatchableCall` handles consumable by `batchSubmitAndWatch` from `@parity/product-sdk-tx`. Combine multiple contract calls — or contract calls mixed with other Asset Hub transactions — into a single atomic `Utility.batch_all` extrinsic.
@@ -126,7 +151,7 @@ const b = registry.publish.prepare("app-two00", "ipfs://...", 0);
126
151
  await batchSubmitAndWatch([a, b], client.raw.assetHub, signer);
127
152
  ```
128
153
 
129
- **`.prepare()` doesn't require a signer.** The resolved origin is used purely for dry-run gas estimation; the batch submission's signer is the dispatched origin at submission time.
154
+ **`.prepare()` doesn't require a signer.** The resolved origin is only used for the dry-run; the batch submission's signer is the dispatched origin at submission time.
130
155
 
131
156
  `PrepareOptions` accepts: `origin`, `value`, `gasLimit`, `storageDepositLimit`. Signer and submission lifecycle options (`signer`, `waitFor`, etc.) are intentionally absent — those belong to the batch submit, not the individual prepared call.
132
157
 
@@ -49,5 +49,5 @@ async function loadPvmContractArtifacts(basePath) {
49
49
  }
50
50
 
51
51
  export { loadPvmContractAbi, loadPvmContractArtifacts, loadPvmContractCode, parsePvmContractAbi };
52
- //# sourceMappingURL=chunk-YZ3YBERU.js.map
53
- //# sourceMappingURL=chunk-YZ3YBERU.js.map
52
+ //# sourceMappingURL=chunk-2UN4YA25.js.map
53
+ //# sourceMappingURL=chunk-2UN4YA25.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/pvm.ts"],"names":[],"mappings":";AAqBO,SAAS,oBAAoB,MAAA,EAA6B;AAC7D,EAAA,IAAI,KAAA,GAAiB,MAAA;AAErB,EAAA,IAAI,iBAAiB,UAAA,EAAY;AAC7B,IAAA,KAAA,GAAQ,IAAI,WAAA,EAAY,CAAE,MAAA,CAAO,KAAK,CAAA;AAAA,EAC1C;AACA,EAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC3B,IAAA,IAAI;AACA,MAAA,KAAA,GAAQ,IAAA,CAAK,MAAM,KAAK,CAAA;AAAA,IAC5B,SAAS,KAAA,EAAO;AACZ,MAAA,MAAM,IAAI,KAAA,CAAM,iCAAA,EAAmC,EAAE,OAAO,CAAA;AAAA,IAChE;AAAA,EACJ;AACA,EAAA,IAAI,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,IAAY,CAAC,MAAM,OAAA,CAAQ,KAAK,CAAA,IAAK,KAAA,IAAS,KAAA,EAAO;AAC/E,IAAA,KAAA,GAAS,KAAA,CAA2B,GAAA;AAAA,EACxC;AACA,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACvB,IAAA,MAAM,IAAI,MAAM,mDAAmD,CAAA;AAAA,EACvE;AACA,EAAA,KAAA,MAAW,SAAS,KAAA,EAAO;AACvB,IAAA,IAAI,CAAC,SAAS,OAAO,KAAA,KAAU,YAAY,OAAQ,KAAA,CAAmB,SAAS,QAAA,EAAU;AACrF,MAAA,MAAM,IAAI,KAAA;AAAA,QACN;AAAA,OACJ;AAAA,IACJ;AACA,IAAA,MAAM,SAAU,KAAA,CAAmB,MAAA;AACnC,IAAA,IAAI,WAAW,MAAA,IAAa,CAAC,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA,EAAG;AAChD,MAAA,MAAM,IAAI,MAAM,yDAAyD,CAAA;AAAA,IAC7E;AAAA,EACJ;AACA,EAAA,OAAO,KAAA;AACX;AAOA,eAAsB,mBAAmB,IAAA,EAAmC;AACxE,EAAA,MAAM,EAAE,QAAA,EAAS,GAAI,MAAM,OAAO,aAAkB,CAAA;AACpD,EAAA,MAAM,GAAA,GAAM,MAAM,QAAA,CAAS,IAAI,CAAA;AAC/B,EAAA,OAAO,oBAAoB,GAAG,CAAA;AAClC;AAYA,eAAsB,oBAAoB,IAAA,EAAmC;AACzE,EAAA,MAAM,EAAE,QAAA,EAAS,GAAI,MAAM,OAAO,aAAkB,CAAA;AACpD,EAAA,MAAM,GAAA,GAAM,MAAM,QAAA,CAAS,IAAI,CAAA;AAC/B,EAAA,OAAO,IAAI,UAAA,CAAW,GAAA,CAAI,QAAQ,GAAA,CAAI,UAAA,EAAY,IAAI,UAAU,CAAA;AACpE;AAYA,eAAsB,yBAAyB,QAAA,EAAiD;AAC5F,EAAA,MAAM,CAAC,GAAA,EAAK,QAAQ,CAAA,GAAI,MAAM,QAAQ,GAAA,CAAI;AAAA,IACtC,kBAAA,CAAmB,CAAA,EAAG,QAAQ,CAAA,SAAA,CAAW,CAAA;AAAA,IACzC,mBAAA,CAAoB,CAAA,EAAG,QAAQ,CAAA,QAAA,CAAU;AAAA,GAC5C,CAAA;AACD,EAAA,OAAO,EAAE,KAAK,QAAA,EAAS;AAC3B","file":"chunk-2UN4YA25.js","sourcesContent":["// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\nimport type { AbiEntry } from \"./types.js\";\n\n/** ABI + PolkaVM bytecode pair emitted by `cargo pvm-contract build`. */\nexport interface PvmContractArtifacts {\n abi: AbiEntry[];\n bytecode: Uint8Array;\n}\n\n/**\n * Parse an in-memory cargo-pvm-contract ABI artifact.\n *\n * Accepts the shapes the toolchain may produce or that products may pass:\n * - parsed JSON array — `AbiEntry[]`\n * - parsed JSON object with an `abi` property — `{ abi: AbiEntry[] }`\n * - JSON string of either of the above\n * - `Uint8Array` containing UTF-8 JSON of either of the above\n *\n * @throws if the input cannot be coerced to a non-empty `AbiEntry[]`.\n */\nexport function parsePvmContractAbi(source: unknown): AbiEntry[] {\n let value: unknown = source;\n\n if (value instanceof Uint8Array) {\n value = new TextDecoder().decode(value);\n }\n if (typeof value === \"string\") {\n try {\n value = JSON.parse(value);\n } catch (cause) {\n throw new Error(\"Invalid PVM ABI: not valid JSON\", { cause });\n }\n }\n if (value && typeof value === \"object\" && !Array.isArray(value) && \"abi\" in value) {\n value = (value as { abi: unknown }).abi;\n }\n if (!Array.isArray(value)) {\n throw new Error(\"Invalid PVM ABI: expected an array of ABI entries\");\n }\n for (const entry of value) {\n if (!entry || typeof entry !== \"object\" || typeof (entry as AbiEntry).type !== \"string\") {\n throw new Error(\n \"Invalid PVM ABI: every entry must have a string `type` (function/event/constructor/...)\",\n );\n }\n const inputs = (entry as AbiEntry).inputs;\n if (inputs !== undefined && !Array.isArray(inputs)) {\n throw new Error(\"Invalid PVM ABI: `inputs` must be an array when present\");\n }\n }\n return value as AbiEntry[];\n}\n\n/**\n * Read a cargo-pvm-contract ABI file from disk and parse it.\n *\n * Node-only. For browser/in-memory inputs use {@link parsePvmContractAbi}.\n */\nexport async function loadPvmContractAbi(path: string): Promise<AbiEntry[]> {\n const { readFile } = await import(\"node:fs/promises\");\n const buf = await readFile(path);\n return parsePvmContractAbi(buf);\n}\n\n/**\n * Read the `.polkavm` bytecode artifact produced by `cargo pvm-contract build`.\n *\n * Returned bytes are ready to hand to `Revive.instantiate_with_code` (or to\n * any future deploy helper layered on top of it). Use this when you already\n * have an ABI in hand (e.g. inline or fetched separately) and only need the\n * PolkaVM blob — otherwise prefer {@link loadPvmContractArtifacts}.\n *\n * Node-only.\n */\nexport async function loadPvmContractCode(path: string): Promise<Uint8Array> {\n const { readFile } = await import(\"node:fs/promises\");\n const buf = await readFile(path);\n return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);\n}\n\n/**\n * Read both the `.abi.json` and `.polkavm` artifacts produced by\n * `cargo pvm-contract build` for a given base path.\n *\n * `basePath` is the path prefix shared by both files — typically\n * `target/<name>.release`. The function reads `${basePath}.abi.json` and\n * `${basePath}.polkavm`.\n *\n * Node-only.\n */\nexport async function loadPvmContractArtifacts(basePath: string): Promise<PvmContractArtifacts> {\n const [abi, bytecode] = await Promise.all([\n loadPvmContractAbi(`${basePath}.abi.json`),\n loadPvmContractCode(`${basePath}.polkavm`),\n ]);\n return { abi, bytecode };\n}\n\nif (import.meta.vitest) {\n const { test, expect, describe, beforeAll, afterAll } = import.meta.vitest;\n\n const sampleAbi: AbiEntry[] = [\n { type: \"constructor\", inputs: [], stateMutability: \"nonpayable\" },\n {\n type: \"function\",\n name: \"increment\",\n inputs: [],\n outputs: [],\n stateMutability: \"nonpayable\",\n },\n {\n type: \"function\",\n name: \"get\",\n inputs: [],\n outputs: [{ name: \"\", type: \"uint32\" }],\n stateMutability: \"view\",\n },\n ];\n\n describe(\"parsePvmContractAbi\", () => {\n test(\"accepts a parsed AbiEntry[] array directly\", () => {\n expect(parsePvmContractAbi(sampleAbi)).toEqual(sampleAbi);\n });\n\n test(\"accepts a wrapped { abi } object\", () => {\n expect(parsePvmContractAbi({ abi: sampleAbi })).toEqual(sampleAbi);\n });\n\n test(\"accepts a JSON string of an array\", () => {\n expect(parsePvmContractAbi(JSON.stringify(sampleAbi))).toEqual(sampleAbi);\n });\n\n test(\"accepts a JSON string of a wrapped object\", () => {\n expect(parsePvmContractAbi(JSON.stringify({ abi: sampleAbi }))).toEqual(sampleAbi);\n });\n\n test(\"accepts a UTF-8 Uint8Array\", () => {\n const bytes = new TextEncoder().encode(JSON.stringify(sampleAbi));\n expect(parsePvmContractAbi(bytes)).toEqual(sampleAbi);\n });\n\n test(\"throws on invalid JSON string\", () => {\n expect(() => parsePvmContractAbi(\"{not json\")).toThrow(/not valid JSON/);\n });\n\n test(\"throws when input is not an array\", () => {\n expect(() => parsePvmContractAbi(42)).toThrow(/expected an array/);\n expect(() => parsePvmContractAbi({ foo: \"bar\" })).toThrow(/expected an array/);\n });\n\n test(\"throws when an entry is missing `type`\", () => {\n expect(() => parsePvmContractAbi([{ name: \"noType\" }])).toThrow(/string `type`/);\n });\n\n test(\"throws when `inputs` is not an array\", () => {\n expect(() =>\n parsePvmContractAbi([{ type: \"function\", inputs: \"not an array\" }]),\n ).toThrow(/`inputs` must be an array/);\n });\n\n test(\"treats null as invalid\", () => {\n expect(() => parsePvmContractAbi(null)).toThrow();\n });\n });\n\n describe(\"loadPvmContractAbi / loadPvmContractArtifacts\", () => {\n // Cover the Node-only filesystem helpers via a real tmpdir round-trip.\n // The cargo-pvm-contract toolchain emits files at\n // target/<name>.release.abi.json\n // target/<name>.release.polkavm\n // — we recreate that layout here.\n let dir = \"\";\n let base = \"\";\n let lonely = \"\";\n let badAbi = \"\";\n // Minimal PolkaVM magic (`PVM\\0`) is enough to exercise the path —\n // we don't validate bytecode contents in the loader.\n const fakeBytecode = new Uint8Array([0x50, 0x56, 0x4d, 0x00, 0x01, 0x02, 0x03]);\n\n beforeAll(async () => {\n const { mkdtempSync, writeFileSync } = await import(\"node:fs\");\n const { tmpdir } = await import(\"node:os\");\n const { join } = await import(\"node:path\");\n dir = mkdtempSync(join(tmpdir(), \"pvm-loader-test-\"));\n base = join(dir, \"counter.release\");\n lonely = join(dir, \"lonely.release\");\n badAbi = join(dir, \"bad.release\");\n writeFileSync(`${base}.abi.json`, JSON.stringify(sampleAbi));\n writeFileSync(`${base}.polkavm`, fakeBytecode);\n writeFileSync(`${lonely}.abi.json`, JSON.stringify(sampleAbi));\n writeFileSync(`${badAbi}.abi.json`, \"{not valid json\");\n });\n\n afterAll(async () => {\n const { rmSync } = await import(\"node:fs\");\n try {\n rmSync(dir, { recursive: true, force: true });\n } catch {\n /* ignore */\n }\n });\n\n test(\"loadPvmContractAbi parses a JSON file from disk\", async () => {\n const abi = await loadPvmContractAbi(`${base}.abi.json`);\n expect(abi).toEqual(sampleAbi);\n });\n\n test(\"loadPvmContractArtifacts reads abi + bytecode pair\", async () => {\n const out = await loadPvmContractArtifacts(base);\n expect(out.abi).toEqual(sampleAbi);\n expect(out.bytecode).toBeInstanceOf(Uint8Array);\n expect(Array.from(out.bytecode)).toEqual(Array.from(fakeBytecode));\n });\n\n test(\"loadPvmContractCode reads only the .polkavm blob\", async () => {\n const code = await loadPvmContractCode(`${base}.polkavm`);\n expect(code).toBeInstanceOf(Uint8Array);\n expect(Array.from(code)).toEqual(Array.from(fakeBytecode));\n });\n\n test(\"loadPvmContractAbi rejects a missing file\", async () => {\n await expect(loadPvmContractAbi(`${base}.does-not-exist`)).rejects.toThrow();\n });\n\n test(\"loadPvmContractArtifacts rejects when bytecode is missing\", async () => {\n // Only `${lonely}.abi.json` exists — `.polkavm` is absent.\n await expect(loadPvmContractArtifacts(lonely)).rejects.toThrow();\n });\n\n test(\"loadPvmContractAbi propagates parse errors with helpful message\", async () => {\n await expect(loadPvmContractAbi(`${badAbi}.abi.json`)).rejects.toThrow(\n /not valid JSON/,\n );\n });\n });\n}\n"]}
package/dist/codegen.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as AbiEntry } from './types-BdHp-xWt.js';
1
+ import { A as AbiEntry } from './types-DzYW54TT.js';
2
2
  import 'polkadot-api';
3
3
  import '@parity/product-sdk-tx';
4
4
  import '@parity/product-sdk-signer';
package/dist/codegen.js CHANGED
@@ -1,4 +1,4 @@
1
- import { loadPvmContractAbi } from './chunk-YZ3YBERU.js';
1
+ import { loadPvmContractAbi } from './chunk-2UN4YA25.js';
2
2
 
3
3
  // src/codegen.ts
4
4
  async function resolveContractTypeInputs(inputs) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/codegen.ts"],"names":[],"mappings":"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","file":"codegen.js","sourcesContent":["import type { AbiEntry, AbiParam } from \"./types.js\";\nimport { loadPvmContractAbi } from \"./pvm.js\";\n\n/** A contract input to {@link generateContractTypes}: either an inline ABI or a `cargo-pvm-contract` artefact path. */\nexport type ContractTypeInput =\n | { library: string; abi: AbiEntry[] }\n | { library: string; abiPath: string };\n\n/**\n * Resolve a heterogeneous list of {@link ContractTypeInput} entries into the\n * `{ library, abi }` shape consumed by {@link generateContractTypes}. Reads any\n * `abiPath` entries off disk via {@link loadPvmContractAbi} (Node only).\n *\n * @example\n * ```ts\n * const resolved = await resolveContractTypeInputs([\n * { library: \"@example/counter\", abiPath: \"./target/counter.release.abi.json\" },\n * { library: \"@example/inline\", abi: counterAbi },\n * ]);\n * const src = generateContractTypes(resolved);\n * ```\n */\nexport async function resolveContractTypeInputs(\n inputs: readonly ContractTypeInput[],\n): Promise<{ library: string; abi: AbiEntry[] }[]> {\n return Promise.all(\n inputs.map(async (input) => {\n if (\"abi\" in input) return { library: input.library, abi: input.abi };\n return { library: input.library, abi: await loadPvmContractAbi(input.abiPath) };\n }),\n );\n}\n\n/** Map a Solidity ABI type to its TypeScript equivalent. */\nfunction mapSolidityType(param: AbiParam): string {\n const t = param.type;\n\n // Dynamic arrays — e.g. uint256[]\n if (t.endsWith(\"[]\")) {\n const inner = { ...param, type: t.slice(0, -2) };\n return `${mapSolidityType(inner)}[]`;\n }\n\n // Fixed-size arrays — e.g. uint256[3]\n const fixedArrayMatch = t.match(/^(.+)\\[(\\d+)\\]$/);\n if (fixedArrayMatch) {\n const inner = { ...param, type: fixedArrayMatch[1] };\n return `${mapSolidityType(inner)}[]`;\n }\n\n // Tuple\n if (t === \"tuple\" && param.components) {\n const fields = param.components.map((c) => `${c.name}: ${mapSolidityType(c)}`);\n return `{ ${fields.join(\"; \")} }`;\n }\n\n // Unsigned integers — uint8/16/32 fit in JS number\n if (/^uint(8|16|32)$/.test(t)) return \"number\";\n if (/^uint\\d*$/.test(t)) return \"bigint\";\n\n // Signed integers\n if (/^int(8|16|32)$/.test(t)) return \"number\";\n if (/^int\\d*$/.test(t)) return \"bigint\";\n\n // Address\n if (t === \"address\") return \"HexString\";\n\n // Bytes — viem's ABI codec accepts hex strings (`0x…`) for both\n // variable-length `bytes` and fixed-length `bytesN`. We surface\n // `SizedHex<N>` for the latter so consumers see the byte width at\n // compile time, even though the runtime check is just on the prefix.\n if (t === \"bytes\") return \"HexString\";\n const bytesMatch = t.match(/^bytes(\\d+)$/);\n if (bytesMatch) return `SizedHex<${bytesMatch[1]}>`;\n\n // Primitives\n if (t === \"bool\") return \"boolean\";\n if (t === \"string\") return \"string\";\n\n return \"unknown\";\n}\n\nfunction generateMethodArgsType(inputs: AbiParam[]): string {\n if (inputs.length === 0) return \"[]\";\n const parts = inputs.map((p) => `${p.name}: ${mapSolidityType(p)}`);\n return `[${parts.join(\", \")}]`;\n}\n\nfunction generateMethodResponseType(outputs: AbiParam[] | undefined): string {\n if (!outputs || outputs.length === 0) return \"undefined\";\n if (outputs.length === 1) return mapSolidityType(outputs[0]);\n const fields = outputs.map((o, i) => {\n const name = o.name || `_${i}`;\n return `${name}: ${mapSolidityType(o)}`;\n });\n return `{ ${fields.join(\"; \")} }`;\n}\n\n/**\n * Generate a TypeScript module augmentation that extends the\n * {@link Contracts} interface with typed method signatures for each\n * installed contract.\n *\n * The output is written to `.cdm/contracts.d.ts` (or equivalent) and\n * augments `\"@parity/product-sdk-contracts\"` so that\n * `ContractManager.getContract()` returns fully-typed handles.\n *\n * Accepts `{ library, abi }` directly. To pull ABIs from\n * `cargo-pvm-contract` build artefacts on disk, use\n * {@link resolveContractTypeInputs} first.\n *\n * @example\n * ```ts\n * // Inline ABIs\n * const src = generateContractTypes([\n * { library: \"@example/counter\", abi },\n * ]);\n * writeFileSync(\".cdm/contracts.d.ts\", src);\n *\n * // From cargo-pvm-contract artefacts\n * const resolved = await resolveContractTypeInputs([\n * { library: \"@example/counter\", abiPath: \"./target/counter.release.abi.json\" },\n * ]);\n * writeFileSync(\".cdm/contracts.d.ts\", generateContractTypes(resolved));\n * ```\n */\nexport function generateContractTypes(contracts: { library: string; abi: AbiEntry[] }[]): string {\n const lines: string[] = [\n \"// Auto-generated by cdm install — do not edit\",\n 'import type { HexString, SizedHex } from \"polkadot-api\";',\n \"\",\n 'declare module \"@parity/product-sdk-contracts\" {',\n \" interface Contracts {\",\n ];\n\n for (const contract of contracts) {\n const methods = contract.abi.filter((e) => e.type === \"function\" && e.name);\n lines.push(` \"${contract.library}\": {`);\n lines.push(\" methods: {\");\n for (const method of methods) {\n const args = generateMethodArgsType(method.inputs);\n const response = generateMethodResponseType(method.outputs);\n lines.push(\n ` ${method.name!}: { args: ${args}; response: ${response} };`,\n );\n }\n lines.push(\" };\");\n lines.push(\" };\");\n }\n\n lines.push(\" }\");\n lines.push(\"}\");\n lines.push(\"\");\n\n return lines.join(\"\\n\");\n}\n\nif (import.meta.vitest) {\n const { test, expect, describe } = import.meta.vitest;\n\n describe(\"mapSolidityType\", () => {\n const cases: [AbiParam, string][] = [\n [{ name: \"x\", type: \"uint8\" }, \"number\"],\n [{ name: \"x\", type: \"uint16\" }, \"number\"],\n [{ name: \"x\", type: \"uint32\" }, \"number\"],\n [{ name: \"x\", type: \"uint64\" }, \"bigint\"],\n [{ name: \"x\", type: \"uint128\" }, \"bigint\"],\n [{ name: \"x\", type: \"uint256\" }, \"bigint\"],\n [{ name: \"x\", type: \"int8\" }, \"number\"],\n [{ name: \"x\", type: \"int32\" }, \"number\"],\n [{ name: \"x\", type: \"int64\" }, \"bigint\"],\n [{ name: \"x\", type: \"int256\" }, \"bigint\"],\n [{ name: \"x\", type: \"address\" }, \"HexString\"],\n [{ name: \"x\", type: \"bool\" }, \"boolean\"],\n [{ name: \"x\", type: \"string\" }, \"string\"],\n [{ name: \"x\", type: \"bytes\" }, \"HexString\"],\n [{ name: \"x\", type: \"bytes32\" }, \"SizedHex<32>\"],\n [{ name: \"x\", type: \"bytes4\" }, \"SizedHex<4>\"],\n [{ name: \"x\", type: \"uint256[]\" }, \"bigint[]\"],\n [{ name: \"x\", type: \"address[]\" }, \"HexString[]\"],\n [{ name: \"x\", type: \"uint256[3]\" }, \"bigint[]\"],\n [{ name: \"x\", type: \"somethingWeird\" }, \"unknown\"],\n ];\n\n for (const [param, expected] of cases) {\n test(`${param.type} → ${expected}`, () => {\n expect(mapSolidityType(param)).toBe(expected);\n });\n }\n\n test(\"tuple with components\", () => {\n const param: AbiParam = {\n name: \"info\",\n type: \"tuple\",\n components: [\n { name: \"addr\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n ],\n };\n expect(mapSolidityType(param)).toBe(\"{ addr: HexString; amount: bigint }\");\n });\n\n test(\"nested array of tuples\", () => {\n const param: AbiParam = {\n name: \"items\",\n type: \"tuple[]\",\n components: [\n { name: \"id\", type: \"uint32\" },\n { name: \"name\", type: \"string\" },\n ],\n };\n expect(mapSolidityType(param)).toBe(\"{ id: number; name: string }[]\");\n });\n });\n\n describe(\"generateMethodArgsType\", () => {\n test(\"empty inputs\", () => {\n expect(generateMethodArgsType([])).toBe(\"[]\");\n });\n\n test(\"single input\", () => {\n expect(generateMethodArgsType([{ name: \"to\", type: \"address\" }])).toBe(\n \"[to: HexString]\",\n );\n });\n\n test(\"multiple inputs\", () => {\n const result = generateMethodArgsType([\n { name: \"to\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n ]);\n expect(result).toBe(\"[to: HexString, amount: bigint]\");\n });\n });\n\n describe(\"generateMethodResponseType\", () => {\n test(\"no outputs\", () => {\n expect(generateMethodResponseType(undefined)).toBe(\"undefined\");\n expect(generateMethodResponseType([])).toBe(\"undefined\");\n });\n\n test(\"single output\", () => {\n expect(generateMethodResponseType([{ name: \"\", type: \"uint32\" }])).toBe(\"number\");\n });\n\n test(\"multiple outputs\", () => {\n const result = generateMethodResponseType([\n { name: \"balance\", type: \"uint256\" },\n { name: \"nonce\", type: \"uint32\" },\n ]);\n expect(result).toBe(\"{ balance: bigint; nonce: number }\");\n });\n\n test(\"unnamed outputs get positional names\", () => {\n const result = generateMethodResponseType([\n { name: \"\", type: \"bool\" },\n { name: \"\", type: \"uint256\" },\n ]);\n expect(result).toBe(\"{ _0: boolean; _1: bigint }\");\n });\n });\n\n describe(\"generateContractTypes\", () => {\n test(\"generates valid module augmentation\", () => {\n const result = generateContractTypes([\n {\n library: \"@example/counter\",\n abi: [\n {\n type: \"constructor\",\n inputs: [],\n stateMutability: \"nonpayable\",\n },\n {\n type: \"function\",\n name: \"getCount\",\n inputs: [],\n outputs: [{ name: \"\", type: \"uint32\" }],\n stateMutability: \"view\",\n },\n {\n type: \"function\",\n name: \"increment\",\n inputs: [],\n outputs: [],\n stateMutability: \"nonpayable\",\n },\n ],\n },\n ]);\n\n expect(result).toContain('declare module \"@parity/product-sdk-contracts\"');\n expect(result).toContain(\"interface Contracts\");\n expect(result).toContain('\"@example/counter\"');\n expect(result).toContain(\"getCount: { args: []; response: number };\");\n expect(result).toContain(\"increment: { args: []; response: undefined };\");\n // Should not include constructor\n expect(result).not.toContain(\"constructor\");\n });\n\n test(\"handles multiple contracts\", () => {\n const result = generateContractTypes([\n {\n library: \"@a/one\",\n abi: [\n {\n type: \"function\",\n name: \"foo\",\n inputs: [{ name: \"x\", type: \"uint256\" }],\n outputs: [{ name: \"\", type: \"bool\" }],\n },\n ],\n },\n {\n library: \"@b/two\",\n abi: [\n {\n type: \"function\",\n name: \"bar\",\n inputs: [],\n outputs: [{ name: \"\", type: \"address\" }],\n },\n ],\n },\n ]);\n\n expect(result).toContain('\"@a/one\"');\n expect(result).toContain('\"@b/two\"');\n expect(result).toContain(\"foo: { args: [x: bigint]; response: boolean };\");\n expect(result).toContain(\"bar: { args: []; response: HexString };\");\n });\n\n test(\"empty contracts list\", () => {\n const result = generateContractTypes([]);\n expect(result).toContain(\"interface Contracts {\");\n expect(result).toContain(\"}\");\n });\n\n test(\"includes polkadot-api type imports\", () => {\n const result = generateContractTypes([]);\n expect(result).toContain(\"import type { HexString, SizedHex }\");\n expect(result).toContain('from \"polkadot-api\"');\n });\n });\n\n describe(\"resolveContractTypeInputs\", () => {\n test(\"passes inline { library, abi } through unchanged\", async () => {\n const abi: AbiEntry[] = [\n {\n type: \"function\",\n name: \"foo\",\n inputs: [],\n outputs: [{ name: \"\", type: \"bool\" }],\n },\n ];\n const out = await resolveContractTypeInputs([{ library: \"@x/foo\", abi }]);\n expect(out).toEqual([{ library: \"@x/foo\", abi }]);\n });\n\n test(\"loads ABI from { abiPath } on disk\", async () => {\n const fs = await import(\"node:fs/promises\");\n const path = await import(\"node:path\");\n const os = await import(\"node:os\");\n const tmp = await fs.mkdtemp(path.join(os.tmpdir(), \"codegen-\"));\n const abi: AbiEntry[] = [\n {\n type: \"function\",\n name: \"ping\",\n inputs: [],\n outputs: [{ name: \"\", type: \"bool\" }],\n },\n ];\n const file = path.join(tmp, \"ping.release.abi.json\");\n await fs.writeFile(file, JSON.stringify(abi), \"utf8\");\n try {\n const out = await resolveContractTypeInputs([\n { library: \"@x/ping\", abiPath: file },\n ]);\n expect(out).toEqual([{ library: \"@x/ping\", abi }]);\n\n // And it composes with generateContractTypes end-to-end:\n const src = generateContractTypes(out);\n expect(src).toContain('\"@x/ping\"');\n expect(src).toContain(\"ping: { args: []; response: boolean };\");\n } finally {\n await fs.rm(tmp, { recursive: true, force: true });\n }\n });\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/codegen.ts"],"names":[],"mappings":"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","file":"codegen.js","sourcesContent":["// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\nimport type { AbiEntry, AbiParam } from \"./types.js\";\nimport { loadPvmContractAbi } from \"./pvm.js\";\n\n/** A contract input to {@link generateContractTypes}: either an inline ABI or a `cargo-pvm-contract` artefact path. */\nexport type ContractTypeInput =\n | { library: string; abi: AbiEntry[] }\n | { library: string; abiPath: string };\n\n/**\n * Resolve a heterogeneous list of {@link ContractTypeInput} entries into the\n * `{ library, abi }` shape consumed by {@link generateContractTypes}. Reads any\n * `abiPath` entries off disk via {@link loadPvmContractAbi} (Node only).\n *\n * @example\n * ```ts\n * const resolved = await resolveContractTypeInputs([\n * { library: \"@example/counter\", abiPath: \"./target/counter.release.abi.json\" },\n * { library: \"@example/inline\", abi: counterAbi },\n * ]);\n * const src = generateContractTypes(resolved);\n * ```\n */\nexport async function resolveContractTypeInputs(\n inputs: readonly ContractTypeInput[],\n): Promise<{ library: string; abi: AbiEntry[] }[]> {\n return Promise.all(\n inputs.map(async (input) => {\n if (\"abi\" in input) return { library: input.library, abi: input.abi };\n return { library: input.library, abi: await loadPvmContractAbi(input.abiPath) };\n }),\n );\n}\n\n/** Map a Solidity ABI type to its TypeScript equivalent. */\nfunction mapSolidityType(param: AbiParam): string {\n const t = param.type;\n\n // Dynamic arrays — e.g. uint256[]\n if (t.endsWith(\"[]\")) {\n const inner = { ...param, type: t.slice(0, -2) };\n return `${mapSolidityType(inner)}[]`;\n }\n\n // Fixed-size arrays — e.g. uint256[3]\n const fixedArrayMatch = t.match(/^(.+)\\[(\\d+)\\]$/);\n if (fixedArrayMatch) {\n const inner = { ...param, type: fixedArrayMatch[1] };\n return `${mapSolidityType(inner)}[]`;\n }\n\n // Tuple\n if (t === \"tuple\" && param.components) {\n const fields = param.components.map((c) => `${c.name}: ${mapSolidityType(c)}`);\n return `{ ${fields.join(\"; \")} }`;\n }\n\n // Unsigned integers — uint8/16/32 fit in JS number\n if (/^uint(8|16|32)$/.test(t)) return \"number\";\n if (/^uint\\d*$/.test(t)) return \"bigint\";\n\n // Signed integers\n if (/^int(8|16|32)$/.test(t)) return \"number\";\n if (/^int\\d*$/.test(t)) return \"bigint\";\n\n // Address\n if (t === \"address\") return \"HexString\";\n\n // Bytes — viem's ABI codec accepts hex strings (`0x…`) for both\n // variable-length `bytes` and fixed-length `bytesN`. We surface\n // `SizedHex<N>` for the latter so consumers see the byte width at\n // compile time, even though the runtime check is just on the prefix.\n if (t === \"bytes\") return \"HexString\";\n const bytesMatch = t.match(/^bytes(\\d+)$/);\n if (bytesMatch) return `SizedHex<${bytesMatch[1]}>`;\n\n // Primitives\n if (t === \"bool\") return \"boolean\";\n if (t === \"string\") return \"string\";\n\n return \"unknown\";\n}\n\nfunction generateMethodArgsType(inputs: AbiParam[]): string {\n if (inputs.length === 0) return \"[]\";\n const parts = inputs.map((p) => `${p.name}: ${mapSolidityType(p)}`);\n return `[${parts.join(\", \")}]`;\n}\n\nfunction generateMethodResponseType(outputs: AbiParam[] | undefined): string {\n if (!outputs || outputs.length === 0) return \"undefined\";\n if (outputs.length === 1) return mapSolidityType(outputs[0]);\n const fields = outputs.map((o, i) => {\n const name = o.name || `_${i}`;\n return `${name}: ${mapSolidityType(o)}`;\n });\n return `{ ${fields.join(\"; \")} }`;\n}\n\n/**\n * Generate a TypeScript module augmentation that extends the\n * {@link Contracts} interface with typed method signatures for each\n * installed contract.\n *\n * The output is written to `.cdm/contracts.d.ts` (or equivalent) and\n * augments `\"@parity/product-sdk-contracts\"` so that\n * `ContractManager.getContract()` returns fully-typed handles.\n *\n * Accepts `{ library, abi }` directly. To pull ABIs from\n * `cargo-pvm-contract` build artefacts on disk, use\n * {@link resolveContractTypeInputs} first.\n *\n * @example\n * ```ts\n * // Inline ABIs\n * const src = generateContractTypes([\n * { library: \"@example/counter\", abi },\n * ]);\n * writeFileSync(\".cdm/contracts.d.ts\", src);\n *\n * // From cargo-pvm-contract artefacts\n * const resolved = await resolveContractTypeInputs([\n * { library: \"@example/counter\", abiPath: \"./target/counter.release.abi.json\" },\n * ]);\n * writeFileSync(\".cdm/contracts.d.ts\", generateContractTypes(resolved));\n * ```\n */\nexport function generateContractTypes(contracts: { library: string; abi: AbiEntry[] }[]): string {\n const lines: string[] = [\n \"// Auto-generated by cdm install — do not edit\",\n 'import type { HexString, SizedHex } from \"polkadot-api\";',\n \"\",\n 'declare module \"@parity/product-sdk-contracts\" {',\n \" interface Contracts {\",\n ];\n\n for (const contract of contracts) {\n const methods = contract.abi.filter((e) => e.type === \"function\" && e.name);\n lines.push(` \"${contract.library}\": {`);\n lines.push(\" methods: {\");\n for (const method of methods) {\n const args = generateMethodArgsType(method.inputs);\n const response = generateMethodResponseType(method.outputs);\n lines.push(\n ` ${method.name!}: { args: ${args}; response: ${response} };`,\n );\n }\n lines.push(\" };\");\n lines.push(\" };\");\n }\n\n lines.push(\" }\");\n lines.push(\"}\");\n lines.push(\"\");\n\n return lines.join(\"\\n\");\n}\n\nif (import.meta.vitest) {\n const { test, expect, describe } = import.meta.vitest;\n\n describe(\"mapSolidityType\", () => {\n const cases: [AbiParam, string][] = [\n [{ name: \"x\", type: \"uint8\" }, \"number\"],\n [{ name: \"x\", type: \"uint16\" }, \"number\"],\n [{ name: \"x\", type: \"uint32\" }, \"number\"],\n [{ name: \"x\", type: \"uint64\" }, \"bigint\"],\n [{ name: \"x\", type: \"uint128\" }, \"bigint\"],\n [{ name: \"x\", type: \"uint256\" }, \"bigint\"],\n [{ name: \"x\", type: \"int8\" }, \"number\"],\n [{ name: \"x\", type: \"int32\" }, \"number\"],\n [{ name: \"x\", type: \"int64\" }, \"bigint\"],\n [{ name: \"x\", type: \"int256\" }, \"bigint\"],\n [{ name: \"x\", type: \"address\" }, \"HexString\"],\n [{ name: \"x\", type: \"bool\" }, \"boolean\"],\n [{ name: \"x\", type: \"string\" }, \"string\"],\n [{ name: \"x\", type: \"bytes\" }, \"HexString\"],\n [{ name: \"x\", type: \"bytes32\" }, \"SizedHex<32>\"],\n [{ name: \"x\", type: \"bytes4\" }, \"SizedHex<4>\"],\n [{ name: \"x\", type: \"uint256[]\" }, \"bigint[]\"],\n [{ name: \"x\", type: \"address[]\" }, \"HexString[]\"],\n [{ name: \"x\", type: \"uint256[3]\" }, \"bigint[]\"],\n [{ name: \"x\", type: \"somethingWeird\" }, \"unknown\"],\n ];\n\n for (const [param, expected] of cases) {\n test(`${param.type} → ${expected}`, () => {\n expect(mapSolidityType(param)).toBe(expected);\n });\n }\n\n test(\"tuple with components\", () => {\n const param: AbiParam = {\n name: \"info\",\n type: \"tuple\",\n components: [\n { name: \"addr\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n ],\n };\n expect(mapSolidityType(param)).toBe(\"{ addr: HexString; amount: bigint }\");\n });\n\n test(\"nested array of tuples\", () => {\n const param: AbiParam = {\n name: \"items\",\n type: \"tuple[]\",\n components: [\n { name: \"id\", type: \"uint32\" },\n { name: \"name\", type: \"string\" },\n ],\n };\n expect(mapSolidityType(param)).toBe(\"{ id: number; name: string }[]\");\n });\n });\n\n describe(\"generateMethodArgsType\", () => {\n test(\"empty inputs\", () => {\n expect(generateMethodArgsType([])).toBe(\"[]\");\n });\n\n test(\"single input\", () => {\n expect(generateMethodArgsType([{ name: \"to\", type: \"address\" }])).toBe(\n \"[to: HexString]\",\n );\n });\n\n test(\"multiple inputs\", () => {\n const result = generateMethodArgsType([\n { name: \"to\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n ]);\n expect(result).toBe(\"[to: HexString, amount: bigint]\");\n });\n });\n\n describe(\"generateMethodResponseType\", () => {\n test(\"no outputs\", () => {\n expect(generateMethodResponseType(undefined)).toBe(\"undefined\");\n expect(generateMethodResponseType([])).toBe(\"undefined\");\n });\n\n test(\"single output\", () => {\n expect(generateMethodResponseType([{ name: \"\", type: \"uint32\" }])).toBe(\"number\");\n });\n\n test(\"multiple outputs\", () => {\n const result = generateMethodResponseType([\n { name: \"balance\", type: \"uint256\" },\n { name: \"nonce\", type: \"uint32\" },\n ]);\n expect(result).toBe(\"{ balance: bigint; nonce: number }\");\n });\n\n test(\"unnamed outputs get positional names\", () => {\n const result = generateMethodResponseType([\n { name: \"\", type: \"bool\" },\n { name: \"\", type: \"uint256\" },\n ]);\n expect(result).toBe(\"{ _0: boolean; _1: bigint }\");\n });\n });\n\n describe(\"generateContractTypes\", () => {\n test(\"generates valid module augmentation\", () => {\n const result = generateContractTypes([\n {\n library: \"@example/counter\",\n abi: [\n {\n type: \"constructor\",\n inputs: [],\n stateMutability: \"nonpayable\",\n },\n {\n type: \"function\",\n name: \"getCount\",\n inputs: [],\n outputs: [{ name: \"\", type: \"uint32\" }],\n stateMutability: \"view\",\n },\n {\n type: \"function\",\n name: \"increment\",\n inputs: [],\n outputs: [],\n stateMutability: \"nonpayable\",\n },\n ],\n },\n ]);\n\n expect(result).toContain('declare module \"@parity/product-sdk-contracts\"');\n expect(result).toContain(\"interface Contracts\");\n expect(result).toContain('\"@example/counter\"');\n expect(result).toContain(\"getCount: { args: []; response: number };\");\n expect(result).toContain(\"increment: { args: []; response: undefined };\");\n // Should not include constructor\n expect(result).not.toContain(\"constructor\");\n });\n\n test(\"handles multiple contracts\", () => {\n const result = generateContractTypes([\n {\n library: \"@a/one\",\n abi: [\n {\n type: \"function\",\n name: \"foo\",\n inputs: [{ name: \"x\", type: \"uint256\" }],\n outputs: [{ name: \"\", type: \"bool\" }],\n },\n ],\n },\n {\n library: \"@b/two\",\n abi: [\n {\n type: \"function\",\n name: \"bar\",\n inputs: [],\n outputs: [{ name: \"\", type: \"address\" }],\n },\n ],\n },\n ]);\n\n expect(result).toContain('\"@a/one\"');\n expect(result).toContain('\"@b/two\"');\n expect(result).toContain(\"foo: { args: [x: bigint]; response: boolean };\");\n expect(result).toContain(\"bar: { args: []; response: HexString };\");\n });\n\n test(\"empty contracts list\", () => {\n const result = generateContractTypes([]);\n expect(result).toContain(\"interface Contracts {\");\n expect(result).toContain(\"}\");\n });\n\n test(\"includes polkadot-api type imports\", () => {\n const result = generateContractTypes([]);\n expect(result).toContain(\"import type { HexString, SizedHex }\");\n expect(result).toContain('from \"polkadot-api\"');\n });\n });\n\n describe(\"resolveContractTypeInputs\", () => {\n test(\"passes inline { library, abi } through unchanged\", async () => {\n const abi: AbiEntry[] = [\n {\n type: \"function\",\n name: \"foo\",\n inputs: [],\n outputs: [{ name: \"\", type: \"bool\" }],\n },\n ];\n const out = await resolveContractTypeInputs([{ library: \"@x/foo\", abi }]);\n expect(out).toEqual([{ library: \"@x/foo\", abi }]);\n });\n\n test(\"loads ABI from { abiPath } on disk\", async () => {\n const fs = await import(\"node:fs/promises\");\n const path = await import(\"node:path\");\n const os = await import(\"node:os\");\n const tmp = await fs.mkdtemp(path.join(os.tmpdir(), \"codegen-\"));\n const abi: AbiEntry[] = [\n {\n type: \"function\",\n name: \"ping\",\n inputs: [],\n outputs: [{ name: \"\", type: \"bool\" }],\n },\n ];\n const file = path.join(tmp, \"ping.release.abi.json\");\n await fs.writeFile(file, JSON.stringify(abi), \"utf8\");\n try {\n const out = await resolveContractTypeInputs([\n { library: \"@x/ping\", abiPath: file },\n ]);\n expect(out).toEqual([{ library: \"@x/ping\", abi }]);\n\n // And it composes with generateContractTypes end-to-end:\n const src = generateContractTypes(out);\n expect(src).toContain('\"@x/ping\"');\n expect(src).toContain(\"ping: { args: []; response: boolean };\");\n } finally {\n await fs.rm(tmp, { recursive: true, force: true });\n }\n });\n });\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,187 +1,9 @@
1
- import { HexString, SS58String, PolkadotClient, PolkadotSigner } from 'polkadot-api';
2
- import { Weight, SubmittableTransaction, TxResult } from '@parity/product-sdk-tx';
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';
3
4
  export { BatchableCall, TxResult } from '@parity/product-sdk-tx';
4
- import { C as CdmJson, a as ContractManagerOptions, b as ContractDefaults, c as Contracts, d as Contract, e as ContractDef, A as AbiEntry, f as ContractOptions } from './types-BdHp-xWt.js';
5
- export { g as AbiParam, h as CdmJsonContract, i as CdmJsonTarget, P as PrepareOptions, Q as QueryOptions, j as QueryResult, T as TxOptions } from './types-BdHp-xWt.js';
6
5
  import '@parity/product-sdk-signer';
7
6
 
8
- /**
9
- * Result of a `Revive.call` extrinsic — present on the typed API as
10
- * `api.tx.Revive.call(args)`. Returned object is a PAPI submittable that
11
- * `submitAndWatch` consumes natively.
12
- *
13
- * `dest` is an H160 hex string and `data` is a raw `Uint8Array`: this matches
14
- * what `polkadot-api` ≥2.0 codecs accept and produce. The class-based
15
- * `Binary` / `FixedSizeBinary` wrappers from `@polkadot-api/substrate-bindings`
16
- * 0.12 are *not* accepted by PAPI 2.x's compatibility check.
17
- */
18
- type ReviveCallTx = (args: {
19
- dest: HexString;
20
- value: bigint;
21
- weight_limit: Weight;
22
- storage_deposit_limit: bigint;
23
- data: Uint8Array;
24
- }) => SubmittableTransaction;
25
- /**
26
- * Dry-run result returned by `ReviveApi.call`. Mirrors the shape exposed by
27
- * descriptors (`paseo-asset-hub`, `polkadot-asset-hub`, `kusama-asset-hub`).
28
- *
29
- * `data` is a raw `Uint8Array` because PAPI ≥2.0 dropped the `Binary` class
30
- * wrapper for `Vec<u8>` codecs.
31
- */
32
- interface ReviveDryRunResult {
33
- weight_consumed: Weight;
34
- weight_required: Weight;
35
- storage_deposit: {
36
- type: "Refund" | "Charge";
37
- value: bigint;
38
- };
39
- max_storage_deposit: {
40
- type: "Refund" | "Charge";
41
- value: bigint;
42
- };
43
- gas_consumed: bigint;
44
- /**
45
- * `success: true` carries `{ flags, data }`; `success: false` carries the
46
- * dispatch error as the chain encoded it.
47
- */
48
- result: {
49
- success: true;
50
- value: {
51
- flags: number;
52
- data: Uint8Array;
53
- };
54
- } | {
55
- success: false;
56
- value: unknown;
57
- };
58
- }
59
- /** Structural shape consumed by `ContractManager` / `createContract`. */
60
- interface ReviveTypedApi {
61
- tx: {
62
- Revive: {
63
- call: ReviveCallTx;
64
- map_account(): SubmittableTransaction;
65
- };
66
- };
67
- query: {
68
- Revive: {
69
- OriginalAccount: {
70
- getValue(address: HexString): Promise<SS58String | undefined>;
71
- };
72
- };
73
- };
74
- apis: {
75
- ReviveApi: {
76
- call(origin: SS58String, dest: HexString, value: bigint, gas_limit: Weight | undefined, storage_deposit_limit: bigint | undefined, input_data: Uint8Array): Promise<ReviveDryRunResult>;
77
- };
78
- };
79
- }
80
- /**
81
- * Signature of a `ReviveApi.call` dry-run, used by the wrapped contract layer
82
- * to estimate weight + storage deposit and surface revert / OOG /
83
- * `AccountNotMapped` failures before a tx is signed.
84
- *
85
- * Identical to `ReviveTypedApi.apis.ReviveApi.call`, but extracted so the
86
- * runtime can route this single hot call through PAPI's *unsafe* API
87
- * (skipping compatibility-token checks) on production runtimes whose
88
- * descriptors lag a chain upgrade — every other surface still uses the
89
- * compat-checked typed API.
90
- */
91
- type ReviveDryRunCall = (origin: SS58String, dest: HexString, value: bigint, gas_limit: Weight | undefined, storage_deposit_limit: bigint | undefined, input_data: Uint8Array) => Promise<ReviveDryRunResult>;
92
- /**
93
- * Runtime handle that drives queries and transactions against a
94
- * pallet-revive-capable chain.
95
- *
96
- * @example
97
- * ```ts
98
- * import { createChainClient } from "@parity/product-sdk-chain-client";
99
- * import { paseo_asset_hub } from "@parity/product-sdk-descriptors/paseo-asset-hub";
100
- * import { createContractRuntimeFromClient } from "@parity/product-sdk-contracts";
101
- *
102
- * const client = await createChainClient({
103
- * chains: { assetHub: paseo_asset_hub },
104
- * rpcs: { assetHub: ["wss://paseo-asset-hub-next-rpc.polkadot.io"] },
105
- * });
106
- * const runtime = createContractRuntimeFromClient(client.raw.assetHub, paseo_asset_hub);
107
- * ```
108
- */
109
- interface ContractRuntime {
110
- readonly api: ReviveTypedApi;
111
- /**
112
- * Dry-run entry point. Production factories route this through the
113
- * *unsafe* API to avoid compatibility-token failures when the descriptor
114
- * trails a runtime upgrade. The {@link createContractRuntime} test factory
115
- * delegates to `api.apis.ReviveApi.call`.
116
- */
117
- readonly dryRunCall: ReviveDryRunCall;
118
- }
119
- /**
120
- * Wrap a typed PAPI API as a `ContractRuntime`. Intended for tests and
121
- * advanced setups where the caller already holds a typed API. Routes the
122
- * dry-run through the typed (compatibility-token-checked) `ReviveApi.call`
123
- * — fine for mocks but susceptible to `Incompatible runtime entry` errors
124
- * on a live chain whose descriptor lags. Prefer
125
- * {@link createContractRuntimeFromClient} for production use.
126
- */
127
- declare function createContractRuntime(api: ReviveTypedApi): ContractRuntime;
128
- /**
129
- * Build a `ContractRuntime` from a raw `PolkadotClient` plus its descriptor.
130
- *
131
- * The typed API powers `tx.Revive.call`, `tx.Revive.map_account`, and
132
- * `query.Revive.OriginalAccount` (extrinsics + storage are tolerant of
133
- * descriptor drift). The runtime-API dry-run, which is *not* tolerant of
134
- * descriptor drift on PAPI's compat-token path, is routed through
135
- * `client.getUnsafeApi()` — bypassing the compat check while preserving
136
- * argument and return shapes.
137
- *
138
- * Use this on every production code path that calls a contract's `.tx()` or
139
- * `.query()` against a live chain.
140
- *
141
- * @example
142
- * ```ts
143
- * import { paseo_asset_hub } from "@parity/product-sdk-descriptors/paseo-asset-hub";
144
- * import { createContractRuntimeFromClient } from "@parity/product-sdk-contracts";
145
- *
146
- * const runtime = createContractRuntimeFromClient(rawClient, paseo_asset_hub);
147
- * ```
148
- */
149
- declare function createContractRuntimeFromClient<TDescriptor>(client: PolkadotClient, descriptor: TDescriptor): ContractRuntime;
150
- /**
151
- * Ensure the SS58 account is mapped to its derived H160 on `pallet-revive`.
152
- *
153
- * `pallet-revive` requires every signing account to have a registered
154
- * `OriginalAccount` mapping before the runtime accepts its `Revive.call`
155
- * extrinsics. The mapping is one-time and cheap. This helper:
156
- *
157
- * 1. Reads `Revive.OriginalAccount` for the H160 derived from `address`.
158
- * 2. Returns `null` if already mapped (idempotent fast-path).
159
- * 3. Otherwise submits `Revive.map_account()` and waits for inclusion.
160
- *
161
- * Call this once per signing account at app startup — after that, every
162
- * subsequent `contract.<method>.tx({ signer })` against the same chain will
163
- * succeed without further mapping work.
164
- *
165
- * @param runtime - The contract runtime (typically `createContractRuntime(...)`).
166
- * @param address - The SS58 address of the account to map.
167
- * @param signer - A signer matching `address`.
168
- * @param options - Optional timeout / status callback (forwarded to the underlying tx).
169
- * @returns The `TxResult` from the mapping extrinsic, or `null` if already mapped.
170
- *
171
- * @example
172
- * ```ts
173
- * import { createContractRuntime, ensureContractAccountMapped } from "@parity/product-sdk-contracts";
174
- *
175
- * const runtime = createContractRuntime(client.getTypedApi(paseo_asset_hub));
176
- * await ensureContractAccountMapped(runtime, signerManager.getState().selectedAccount!.address, signer);
177
- * // now safe to call contract.<method>.tx({ signer })
178
- * ```
179
- */
180
- declare function ensureContractAccountMapped(runtime: ContractRuntime, address: SS58String, signer: PolkadotSigner, options?: {
181
- timeoutMs?: number;
182
- onStatus?: (s: string) => void;
183
- }): Promise<TxResult | null>;
184
-
185
7
  /**
186
8
  * Manages typed contract interactions backed by a `cdm.json` manifest.
187
9
  *
@@ -231,7 +53,7 @@ declare class ContractManager {
231
53
  * @param descriptor - The chain descriptor used to derive the typed API.
232
54
  * @param options - Optional configuration (signerManager, defaults).
233
55
  */
234
- static fromClient<TDescriptor>(cdmJson: CdmJson, client: PolkadotClient, descriptor: TDescriptor, options?: ContractManagerOptions): ContractManager;
56
+ static fromClient<TDescriptor>(cdmJson: CdmJson, client: PolkadotClient, descriptor: TDescriptor, options?: ContractManagerOptions & ContractRuntimeOptions): ContractManager;
235
57
  private getContractData;
236
58
  /**
237
59
  * Get a typed contract handle.
@@ -282,7 +104,7 @@ declare function createContract(runtime: ContractRuntime, address: HexString, ab
282
104
  * const { value } = await counter.getCount.query();
283
105
  * ```
284
106
  */
285
- declare function createContractFromClient<TDescriptor>(client: PolkadotClient, descriptor: TDescriptor, address: HexString, abi: AbiEntry[], options?: ContractOptions): Contract<ContractDef>;
107
+ declare function createContractFromClient<TDescriptor>(client: PolkadotClient, descriptor: TDescriptor, address: HexString, abi: AbiEntry[], options?: ContractOptions & ContractRuntimeOptions): Contract<ContractDef>;
286
108
 
287
109
  /** Base class for all contract errors. Use `instanceof ContractError` to catch any contract-related error. */
288
110
  declare class ContractError extends Error {
@@ -312,5 +134,33 @@ declare class ContractDryRunFailedError extends ContractError {
312
134
  readonly dispatchError: unknown;
313
135
  constructor(methodName: string, dispatchError: unknown);
314
136
  }
137
+ /** viem-decoded standard or ABI-defined contract error. */
138
+ interface DecodedContractRevert {
139
+ errorName: string;
140
+ args: readonly unknown[] | undefined;
141
+ }
142
+ /**
143
+ * Tagged-enum value surfaced on `QueryResult.value` when a contract reverts
144
+ * via the REVERT flag. The discriminant is intentionally distinct from
145
+ * `pallet-revive`'s bare `{ type: "ContractReverted" }` dispatch-error variant,
146
+ * which is the other path that can populate `QueryResult.value` on failure.
147
+ */
148
+ interface ContractRevertInfo {
149
+ type: "ContractRevertedWithPayload";
150
+ data: HexString;
151
+ reason?: string;
152
+ decoded?: DecodedContractRevert;
153
+ }
154
+ /** A contract call returned with the `REVERT` flag set on a dispatched-OK call. */
155
+ declare class ContractRevertedError extends ContractError {
156
+ readonly methodName: string;
157
+ readonly data: HexString;
158
+ readonly reason?: string;
159
+ readonly decoded?: DecodedContractRevert;
160
+ constructor(methodName: string, data: HexString, info?: {
161
+ reason?: string;
162
+ decoded?: DecodedContractRevert;
163
+ });
164
+ }
315
165
 
316
- export { AbiEntry, CdmJson, Contract, ContractDef, ContractDefaults, ContractDryRunFailedError, ContractError, ContractManager, ContractManagerOptions, ContractNotFoundError, ContractOptions, type ContractRuntime, ContractSignerMissingError, Contracts, type ReviveDryRunCall, type ReviveDryRunResult, type ReviveTypedApi, createContract, createContractFromClient, createContractRuntime, createContractRuntimeFromClient, ensureContractAccountMapped };
166
+ export { AbiEntry, CdmJson, Contract, ContractDef, ContractDefaults, ContractDryRunFailedError, ContractError, ContractManager, ContractManagerOptions, ContractNotFoundError, ContractOptions, type ContractRevertInfo, ContractRevertedError, ContractRuntime, ContractRuntimeOptions, ContractSignerMissingError, Contracts, type DecodedContractRevert, createContract, createContractFromClient };