@parity/product-sdk-contracts 0.8.3 → 0.9.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/dist/codegen.d.ts +5 -3
- package/dist/index.d.ts +15 -74
- package/dist/index.js +58 -42
- package/dist/index.js.map +1 -1
- package/dist/pvm.d.ts +5 -3
- package/dist/testing.d.ts +85 -0
- package/dist/testing.js +99 -0
- package/dist/testing.js.map +1 -0
- package/dist/{types-pkS1R9Bk.d.ts → types-BKcrGoiN.d.ts} +91 -9
- package/package.json +12 -5
package/dist/pvm.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { A as AbiEntry } from './types-
|
|
2
|
-
import '
|
|
3
|
-
import '@parity/product-sdk-tx';
|
|
1
|
+
import { A as AbiEntry } from './types-BKcrGoiN.js';
|
|
2
|
+
import '@parity/result';
|
|
4
3
|
import '@parity/product-sdk-signer';
|
|
4
|
+
import '@parity/product-sdk-tx';
|
|
5
|
+
import 'polkadot-api';
|
|
6
|
+
import '@parity/product-sdk-errors';
|
|
5
7
|
|
|
6
8
|
/** ABI + PolkaVM bytecode pair emitted by `cargo pvm-contract build`. */
|
|
7
9
|
interface PvmContractArtifacts {
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { HexString, SS58String } from 'polkadot-api';
|
|
2
|
+
import { Weight } from '@parity/product-sdk-tx';
|
|
3
|
+
import { A as AbiEntry, a as ContractRuntime, v as ReviveDryRunResult } from './types-BKcrGoiN.js';
|
|
4
|
+
import '@parity/result';
|
|
5
|
+
import '@parity/product-sdk-signer';
|
|
6
|
+
import '@parity/product-sdk-errors';
|
|
7
|
+
|
|
8
|
+
/** Options for {@link fakeDryRunResult}. */
|
|
9
|
+
interface FakeDryRunResultOptions {
|
|
10
|
+
/** Raw ABI-encoded return bytes. Ignored when the runtime encodes for you. */
|
|
11
|
+
data?: Uint8Array;
|
|
12
|
+
/** Simulate a revert: `true` for an empty reason, a string for a UTF-8 reason. Sets the revert flag. */
|
|
13
|
+
revert?: boolean | string;
|
|
14
|
+
/** Simulate a dispatch failure (`result.success: false`) carrying this payload. */
|
|
15
|
+
failure?: unknown;
|
|
16
|
+
/** Weight surfaced as a query's `gasRequired`. Default `{ ref_time: 1n, proof_size: 1n }`. */
|
|
17
|
+
weightRequired?: Weight;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Build a {@link ReviveDryRunResult}. Defaults to a successful, empty return;
|
|
21
|
+
* pass `data` for a raw payload, `revert` for a revert, or `failure` for a
|
|
22
|
+
* dispatch error.
|
|
23
|
+
*/
|
|
24
|
+
declare function fakeDryRunResult(options?: FakeDryRunResultOptions): ReviveDryRunResult;
|
|
25
|
+
/** The dry-run / query a fake runtime was asked to perform. */
|
|
26
|
+
interface DecodedContractCall {
|
|
27
|
+
origin: SS58String;
|
|
28
|
+
dest: HexString;
|
|
29
|
+
value: bigint;
|
|
30
|
+
data: Uint8Array;
|
|
31
|
+
/** Decoded function name — present only when `abi` was supplied. */
|
|
32
|
+
functionName?: string;
|
|
33
|
+
/** Decoded arguments — present only when `abi` was supplied. */
|
|
34
|
+
args?: unknown[];
|
|
35
|
+
}
|
|
36
|
+
/** The extrinsic a fake runtime was asked to build via `.tx()` / `.prepare()`. */
|
|
37
|
+
interface BuiltContractCall {
|
|
38
|
+
dest: HexString;
|
|
39
|
+
value: bigint;
|
|
40
|
+
weight_limit: Weight;
|
|
41
|
+
storage_deposit_limit: bigint;
|
|
42
|
+
data: Uint8Array;
|
|
43
|
+
}
|
|
44
|
+
/** A fake {@link ContractRuntime} with an inspection surface for tests. */
|
|
45
|
+
interface FakeContractRuntime extends ContractRuntime {
|
|
46
|
+
/** Every dry-run / query the runtime handled, in order. */
|
|
47
|
+
readonly calls: ReadonlyArray<DecodedContractCall>;
|
|
48
|
+
/** Clear the recorded calls. */
|
|
49
|
+
reset(): void;
|
|
50
|
+
}
|
|
51
|
+
/** Options for {@link createFakeContractRuntime}. */
|
|
52
|
+
interface CreateFakeContractRuntimeOptions {
|
|
53
|
+
/**
|
|
54
|
+
* ABI used to decode inbound calldata (so `onQuery` gets `functionName` /
|
|
55
|
+
* `args`) and to encode a value `onQuery` returns. Omit only if `onQuery`
|
|
56
|
+
* returns raw bytes or a {@link fakeDryRunResult}.
|
|
57
|
+
*/
|
|
58
|
+
abi?: AbiEntry[];
|
|
59
|
+
/**
|
|
60
|
+
* Handle each query / dry-run. Return the value to decode to (encoded via
|
|
61
|
+
* `abi`), raw bytes, or a {@link fakeDryRunResult}. Default: empty success.
|
|
62
|
+
*/
|
|
63
|
+
onQuery?: (call: DecodedContractCall) => unknown;
|
|
64
|
+
/** Called with the extrinsic `.tx()` / `.prepare()` builds. */
|
|
65
|
+
onCall?: (call: BuiltContractCall) => void;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Create an in-memory {@link ContractRuntime}.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```ts
|
|
72
|
+
* import { createContract } from "@parity/product-sdk-contracts";
|
|
73
|
+
* import { createFakeContractRuntime } from "@parity/product-sdk-contracts/testing";
|
|
74
|
+
*
|
|
75
|
+
* const runtime = createFakeContractRuntime({
|
|
76
|
+
* abi: erc20Abi,
|
|
77
|
+
* onQuery: ({ functionName }) => (functionName === "balanceOf" ? 1000n : 0n),
|
|
78
|
+
* });
|
|
79
|
+
* const token = createContract(runtime, ADDRESS, erc20Abi);
|
|
80
|
+
* const { value } = await token.balanceOf.query(HOLDER); // 1000n
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
declare function createFakeContractRuntime(options?: CreateFakeContractRuntimeOptions): FakeContractRuntime;
|
|
84
|
+
|
|
85
|
+
export { type BuiltContractCall, type CreateFakeContractRuntimeOptions, type DecodedContractCall, type FakeContractRuntime, type FakeDryRunResultOptions, createFakeContractRuntime, fakeDryRunResult };
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { decodeFunctionData, bytesToHex, hexToBytes, encodeFunctionResult } from 'viem';
|
|
2
|
+
|
|
3
|
+
// src/testing.ts
|
|
4
|
+
var REVERT_FLAG = 1;
|
|
5
|
+
var textEncoder = new TextEncoder();
|
|
6
|
+
function fakeDryRunResult(options) {
|
|
7
|
+
const base = {
|
|
8
|
+
weight_consumed: { ref_time: 0n, proof_size: 0n },
|
|
9
|
+
weight_required: options?.weightRequired ?? { ref_time: 1n, proof_size: 1n },
|
|
10
|
+
storage_deposit: { type: "Refund", value: 0n },
|
|
11
|
+
max_storage_deposit: { type: "Refund", value: 0n },
|
|
12
|
+
gas_consumed: 0n
|
|
13
|
+
};
|
|
14
|
+
if (options?.failure !== void 0) {
|
|
15
|
+
return { ...base, result: { success: false, value: options.failure } };
|
|
16
|
+
}
|
|
17
|
+
if (options?.revert !== void 0 && options.revert !== false) {
|
|
18
|
+
const reason = typeof options.revert === "string" ? options.revert : "";
|
|
19
|
+
return {
|
|
20
|
+
...base,
|
|
21
|
+
result: {
|
|
22
|
+
success: true,
|
|
23
|
+
value: { flags: REVERT_FLAG, data: textEncoder.encode(reason) }
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
...base,
|
|
29
|
+
result: { success: true, value: { flags: 0, data: options?.data ?? new Uint8Array(0) } }
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function isDryRunResult(value) {
|
|
33
|
+
return typeof value === "object" && value !== null && "result" in value && "weight_required" in value;
|
|
34
|
+
}
|
|
35
|
+
function createFakeContractRuntime(options) {
|
|
36
|
+
const abi = options?.abi;
|
|
37
|
+
const calls = [];
|
|
38
|
+
const dryRunCall = async (origin, dest, value, _gasLimit, _storageDepositLimit, data) => {
|
|
39
|
+
let functionName;
|
|
40
|
+
let args;
|
|
41
|
+
if (abi) {
|
|
42
|
+
const decoded = decodeFunctionData({
|
|
43
|
+
abi,
|
|
44
|
+
data: bytesToHex(data)
|
|
45
|
+
});
|
|
46
|
+
functionName = decoded.functionName;
|
|
47
|
+
args = decoded.args ? [...decoded.args] : [];
|
|
48
|
+
}
|
|
49
|
+
const call = { origin, dest, value, data, functionName, args };
|
|
50
|
+
calls.push(call);
|
|
51
|
+
const out = options?.onQuery ? options.onQuery(call) : void 0;
|
|
52
|
+
if (isDryRunResult(out)) return out;
|
|
53
|
+
if (out instanceof Uint8Array) return fakeDryRunResult({ data: out });
|
|
54
|
+
if (out === void 0) return fakeDryRunResult();
|
|
55
|
+
if (!abi || !functionName) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
"createFakeContractRuntime: onQuery returned a value but no abi is set to encode it. Pass `abi`, or return a Uint8Array or fakeDryRunResult()."
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
const encoded = hexToBytes(
|
|
61
|
+
encodeFunctionResult({
|
|
62
|
+
abi,
|
|
63
|
+
functionName,
|
|
64
|
+
result: out
|
|
65
|
+
})
|
|
66
|
+
);
|
|
67
|
+
return fakeDryRunResult({ data: encoded });
|
|
68
|
+
};
|
|
69
|
+
const api = {
|
|
70
|
+
tx: {
|
|
71
|
+
Revive: {
|
|
72
|
+
call: (built) => {
|
|
73
|
+
options?.onCall?.(built);
|
|
74
|
+
return {};
|
|
75
|
+
},
|
|
76
|
+
map_account: () => ({})
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
query: { Revive: { OriginalAccount: { getValue: async () => void 0 } } },
|
|
80
|
+
apis: {
|
|
81
|
+
ReviveApi: {
|
|
82
|
+
// Same signature as dryRunCall (it ignores the 7th `options` arg).
|
|
83
|
+
call: dryRunCall
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
return {
|
|
88
|
+
api,
|
|
89
|
+
dryRunCall,
|
|
90
|
+
calls,
|
|
91
|
+
reset() {
|
|
92
|
+
calls.length = 0;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export { createFakeContractRuntime, fakeDryRunResult };
|
|
98
|
+
//# sourceMappingURL=testing.js.map
|
|
99
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/testing.ts"],"names":[],"mappings":"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","file":"testing.js","sourcesContent":["// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Test fakes for `@parity/product-sdk-contracts`.\n *\n * `createFakeContractRuntime` is an in-memory `ContractRuntime` for\n * `createContract(runtime, address, abi)` — test `.query()` / `.prepare()` with\n * no chain and no deployed contract. `fakeDryRunResult` builds the fiddly\n * `ReviveDryRunResult` when you need full control over reverts / failures.\n *\n * @packageDocumentation\n */\nimport {\n type Abi as ViemAbi,\n bytesToHex,\n decodeFunctionData,\n encodeFunctionResult,\n hexToBytes,\n} from \"viem\";\nimport type { HexString, SS58String } from \"polkadot-api\";\nimport type { SubmittableTransaction, Weight } from \"@parity/product-sdk-tx\";\n\nimport type { ContractRuntime, ReviveDryRunResult, ReviveTypedApi } from \"./runtime.js\";\nimport type { AbiEntry } from \"./types.js\";\n\n/** pallet-revive sets bit 0 of `flags` when a call reverts (mirror of wrap.ts). */\nconst REVERT_FLAG = 1;\n\nconst textEncoder = new TextEncoder();\n\n/** Options for {@link fakeDryRunResult}. */\nexport interface FakeDryRunResultOptions {\n /** Raw ABI-encoded return bytes. Ignored when the runtime encodes for you. */\n data?: Uint8Array;\n /** Simulate a revert: `true` for an empty reason, a string for a UTF-8 reason. Sets the revert flag. */\n revert?: boolean | string;\n /** Simulate a dispatch failure (`result.success: false`) carrying this payload. */\n failure?: unknown;\n /** Weight surfaced as a query's `gasRequired`. Default `{ ref_time: 1n, proof_size: 1n }`. */\n weightRequired?: Weight;\n}\n\n/**\n * Build a {@link ReviveDryRunResult}. Defaults to a successful, empty return;\n * pass `data` for a raw payload, `revert` for a revert, or `failure` for a\n * dispatch error.\n */\nexport function fakeDryRunResult(options?: FakeDryRunResultOptions): ReviveDryRunResult {\n const base = {\n weight_consumed: { ref_time: 0n, proof_size: 0n },\n weight_required: options?.weightRequired ?? { ref_time: 1n, proof_size: 1n },\n storage_deposit: { type: \"Refund\" as const, value: 0n },\n max_storage_deposit: { type: \"Refund\" as const, value: 0n },\n gas_consumed: 0n,\n };\n if (options?.failure !== undefined) {\n return { ...base, result: { success: false, value: options.failure } };\n }\n if (options?.revert !== undefined && options.revert !== false) {\n const reason = typeof options.revert === \"string\" ? options.revert : \"\";\n return {\n ...base,\n result: {\n success: true,\n value: { flags: REVERT_FLAG, data: textEncoder.encode(reason) },\n },\n };\n }\n return {\n ...base,\n result: { success: true, value: { flags: 0, data: options?.data ?? new Uint8Array(0) } },\n };\n}\n\nfunction isDryRunResult(value: unknown): value is ReviveDryRunResult {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"result\" in value &&\n \"weight_required\" in value\n );\n}\n\n/** The dry-run / query a fake runtime was asked to perform. */\nexport interface DecodedContractCall {\n origin: SS58String;\n dest: HexString;\n value: bigint;\n data: Uint8Array;\n /** Decoded function name — present only when `abi` was supplied. */\n functionName?: string;\n /** Decoded arguments — present only when `abi` was supplied. */\n args?: unknown[];\n}\n\n/** The extrinsic a fake runtime was asked to build via `.tx()` / `.prepare()`. */\nexport interface BuiltContractCall {\n dest: HexString;\n value: bigint;\n weight_limit: Weight;\n storage_deposit_limit: bigint;\n data: Uint8Array;\n}\n\n/** A fake {@link ContractRuntime} with an inspection surface for tests. */\nexport interface FakeContractRuntime extends ContractRuntime {\n /** Every dry-run / query the runtime handled, in order. */\n readonly calls: ReadonlyArray<DecodedContractCall>;\n /** Clear the recorded calls. */\n reset(): void;\n}\n\n/** Options for {@link createFakeContractRuntime}. */\nexport interface CreateFakeContractRuntimeOptions {\n /**\n * ABI used to decode inbound calldata (so `onQuery` gets `functionName` /\n * `args`) and to encode a value `onQuery` returns. Omit only if `onQuery`\n * returns raw bytes or a {@link fakeDryRunResult}.\n */\n abi?: AbiEntry[];\n /**\n * Handle each query / dry-run. Return the value to decode to (encoded via\n * `abi`), raw bytes, or a {@link fakeDryRunResult}. Default: empty success.\n */\n onQuery?: (call: DecodedContractCall) => unknown;\n /** Called with the extrinsic `.tx()` / `.prepare()` builds. */\n onCall?: (call: BuiltContractCall) => void;\n}\n\n/**\n * Create an in-memory {@link ContractRuntime}.\n *\n * @example\n * ```ts\n * import { createContract } from \"@parity/product-sdk-contracts\";\n * import { createFakeContractRuntime } from \"@parity/product-sdk-contracts/testing\";\n *\n * const runtime = createFakeContractRuntime({\n * abi: erc20Abi,\n * onQuery: ({ functionName }) => (functionName === \"balanceOf\" ? 1000n : 0n),\n * });\n * const token = createContract(runtime, ADDRESS, erc20Abi);\n * const { value } = await token.balanceOf.query(HOLDER); // 1000n\n * ```\n */\nexport function createFakeContractRuntime(\n options?: CreateFakeContractRuntimeOptions,\n): FakeContractRuntime {\n const abi = options?.abi;\n const calls: DecodedContractCall[] = [];\n\n const dryRunCall: ContractRuntime[\"dryRunCall\"] = async (\n origin,\n dest,\n value,\n _gasLimit,\n _storageDepositLimit,\n data,\n ) => {\n let functionName: string | undefined;\n let args: unknown[] | undefined;\n if (abi) {\n const decoded = decodeFunctionData({\n abi: abi as unknown as ViemAbi,\n data: bytesToHex(data),\n });\n functionName = decoded.functionName;\n args = decoded.args ? [...decoded.args] : [];\n }\n const call: DecodedContractCall = { origin, dest, value, data, functionName, args };\n calls.push(call);\n\n const out = options?.onQuery ? options.onQuery(call) : undefined;\n if (isDryRunResult(out)) return out;\n if (out instanceof Uint8Array) return fakeDryRunResult({ data: out });\n if (out === undefined) return fakeDryRunResult();\n\n if (!abi || !functionName) {\n throw new Error(\n \"createFakeContractRuntime: onQuery returned a value but no abi is set to encode it. Pass `abi`, or return a Uint8Array or fakeDryRunResult().\",\n );\n }\n const encoded = hexToBytes(\n encodeFunctionResult({\n abi: abi as unknown as ViemAbi,\n functionName,\n result: out,\n }),\n );\n return fakeDryRunResult({ data: encoded });\n };\n\n const api: ReviveTypedApi = {\n tx: {\n Revive: {\n call: (built) => {\n options?.onCall?.(built);\n return {} as unknown as SubmittableTransaction;\n },\n map_account: () => ({}) as unknown as SubmittableTransaction,\n },\n },\n query: { Revive: { OriginalAccount: { getValue: async () => undefined } } },\n apis: {\n ReviveApi: {\n // Same signature as dryRunCall (it ignores the 7th `options` arg).\n call: dryRunCall,\n },\n },\n };\n\n return {\n api,\n dryRunCall,\n calls,\n reset() {\n calls.length = 0;\n },\n } satisfies FakeContractRuntime;\n}\n\nif (import.meta.vitest) {\n // Round-trip guard: drive the fake through the *real* createContract.\n const { describe, test, expect } = import.meta.vitest;\n const { createContract } = await import(\"./manager.js\");\n\n const abi: AbiEntry[] = [\n {\n type: \"function\",\n name: \"balanceOf\",\n stateMutability: \"view\",\n inputs: [{ name: \"owner\", type: \"address\" }],\n outputs: [{ name: \"\", type: \"uint256\" }],\n },\n {\n type: \"function\",\n name: \"transfer\",\n stateMutability: \"nonpayable\",\n inputs: [\n { name: \"to\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n ],\n outputs: [{ name: \"\", type: \"bool\" }],\n },\n ];\n\n const CONTRACT = `0x${\"22\".repeat(20)}` as HexString;\n const HOLDER = `0x${\"11\".repeat(20)}` as HexString;\n\n // Minimal typed handle for the two ABI methods above.\n type TestToken = {\n balanceOf: {\n query: (\n owner: HexString,\n ) => Promise<{ success: true; value: bigint } | { success: false; value: unknown }>;\n };\n transfer: {\n prepare: (\n to: HexString,\n amount: bigint,\n opts?: { gasLimit?: Weight; storageDepositLimit?: bigint },\n ) => Promise<unknown>;\n };\n };\n const makeToken = (runtime: ContractRuntime) =>\n createContract(runtime, CONTRACT, abi) as unknown as TestToken;\n\n describe(\"createFakeContractRuntime\", () => {\n test(\"query decodes a value the runtime encodes from onQuery\", async () => {\n const runtime = createFakeContractRuntime({\n abi,\n onQuery: ({ functionName }) => (functionName === \"balanceOf\" ? 1000n : 0n),\n });\n const token = makeToken(runtime);\n\n const res = await token.balanceOf.query(HOLDER);\n expect(res.success).toBe(true);\n if (res.success) expect(res.value).toBe(1000n);\n expect(runtime.calls[0]?.functionName).toBe(\"balanceOf\");\n });\n\n test(\"query surfaces a revert as success:false\", async () => {\n const runtime = createFakeContractRuntime({\n abi,\n onQuery: () => fakeDryRunResult({ revert: \"InsufficientBalance\" }),\n });\n const res = await makeToken(runtime).balanceOf.query(HOLDER);\n expect(res.success).toBe(false);\n });\n\n test(\"query surfaces a dispatch failure with its payload\", async () => {\n const runtime = createFakeContractRuntime({\n abi,\n onQuery: () => fakeDryRunResult({ failure: { type: \"AccountNotMapped\" } }),\n });\n const res = await makeToken(runtime).balanceOf.query(HOLDER);\n expect(res.success).toBe(false);\n expect(res.value).toEqual({ type: \"AccountNotMapped\" });\n });\n\n test(\"prepare builds an extrinsic that onCall captures\", async () => {\n let captured: BuiltContractCall | undefined;\n const runtime = createFakeContractRuntime({\n abi,\n onCall: (c) => {\n captured = c;\n },\n });\n // Supply both limits so the sizing dry-run is skipped (no origin needed).\n await makeToken(runtime).transfer.prepare(HOLDER, 5n, {\n gasLimit: { ref_time: 100n, proof_size: 200n },\n storageDepositLimit: 0n,\n });\n\n expect(captured?.dest).toBe(CONTRACT);\n expect(captured?.value).toBe(0n);\n expect(captured?.weight_limit).toEqual({ ref_time: 100n, proof_size: 200n });\n expect(captured?.data).toBeInstanceOf(Uint8Array);\n });\n });\n}\n"]}
|
|
@@ -1,6 +1,80 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Weight, SubmittableTransaction, TxResult, SubmitOptions, BatchableCall } from '@parity/product-sdk-tx';
|
|
1
|
+
import { Result } from '@parity/result';
|
|
3
2
|
import { SignerManager } from '@parity/product-sdk-signer';
|
|
3
|
+
import { Weight, SubmittableTransaction, TxResult, TxError, SubmitOptions, BatchableCall } from '@parity/product-sdk-tx';
|
|
4
|
+
import { HexString, SS58String, PolkadotClient, PolkadotSigner } from 'polkadot-api';
|
|
5
|
+
import { SdkError } from '@parity/product-sdk-errors';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Base class for all contract errors. Use `instanceof ContractError` to catch any
|
|
9
|
+
* contract-related error. Implements the cross-package {@link SdkError} marker so
|
|
10
|
+
* `isSdkError(e)` also recognizes it.
|
|
11
|
+
*/
|
|
12
|
+
declare class ContractError extends Error implements SdkError {
|
|
13
|
+
readonly isSdkError: true;
|
|
14
|
+
readonly source = "contracts";
|
|
15
|
+
constructor(message: string, options?: ErrorOptions);
|
|
16
|
+
}
|
|
17
|
+
/** No signer was available for a transaction call. */
|
|
18
|
+
declare class ContractSignerMissingError extends ContractError {
|
|
19
|
+
constructor();
|
|
20
|
+
}
|
|
21
|
+
/** A contract was not found in the cdm.json manifest. */
|
|
22
|
+
declare class ContractNotFoundError extends ContractError {
|
|
23
|
+
readonly library: string;
|
|
24
|
+
constructor(library: string);
|
|
25
|
+
}
|
|
26
|
+
/** Live CDM registry address resolution failed. */
|
|
27
|
+
declare class ContractLiveAddressResolutionError extends ContractError {
|
|
28
|
+
readonly library: string | undefined;
|
|
29
|
+
readonly detail: unknown;
|
|
30
|
+
constructor(message: string, options?: {
|
|
31
|
+
library?: string;
|
|
32
|
+
detail?: unknown;
|
|
33
|
+
cause?: unknown;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A pre-flight `ReviveApi.call` dry-run reported failure. Thrown from the `.tx()`
|
|
38
|
+
* path before the extrinsic is built — prevents callers from paying gas on a
|
|
39
|
+
* transaction the chain already told us would revert.
|
|
40
|
+
*
|
|
41
|
+
* `dispatchError` carries the chain's encoded error (typically `ModuleError`,
|
|
42
|
+
* `ContractReverted`, `OutOfGas`, or `AccountNotMapped` — see the `Revive`
|
|
43
|
+
* pallet error variants).
|
|
44
|
+
*/
|
|
45
|
+
declare class ContractDryRunFailedError extends ContractError {
|
|
46
|
+
readonly methodName: string;
|
|
47
|
+
readonly dispatchError: unknown;
|
|
48
|
+
constructor(methodName: string, dispatchError: unknown);
|
|
49
|
+
}
|
|
50
|
+
/** viem-decoded standard or ABI-defined contract error. */
|
|
51
|
+
interface DecodedContractRevert {
|
|
52
|
+
errorName: string;
|
|
53
|
+
args: readonly unknown[] | undefined;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Tagged-enum value surfaced on `QueryResult.value` when a contract reverts
|
|
57
|
+
* via the REVERT flag. The discriminant is intentionally distinct from
|
|
58
|
+
* `pallet-revive`'s bare `{ type: "ContractReverted" }` dispatch-error variant,
|
|
59
|
+
* which is the other path that can populate `QueryResult.value` on failure.
|
|
60
|
+
*/
|
|
61
|
+
interface ContractRevertInfo {
|
|
62
|
+
type: "ContractRevertedWithPayload";
|
|
63
|
+
data: HexString;
|
|
64
|
+
reason?: string;
|
|
65
|
+
decoded?: DecodedContractRevert;
|
|
66
|
+
}
|
|
67
|
+
/** A contract call returned with the `REVERT` flag set on a dispatched-OK call. */
|
|
68
|
+
declare class ContractRevertedError extends ContractError {
|
|
69
|
+
readonly methodName: string;
|
|
70
|
+
readonly data: HexString;
|
|
71
|
+
readonly reason?: string;
|
|
72
|
+
readonly decoded?: DecodedContractRevert;
|
|
73
|
+
constructor(methodName: string, data: HexString, info?: {
|
|
74
|
+
reason?: string;
|
|
75
|
+
decoded?: DecodedContractRevert;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
4
78
|
|
|
5
79
|
/**
|
|
6
80
|
* Result of a `Revive.call` extrinsic — present on the typed API as
|
|
@@ -202,7 +276,8 @@ declare function createContractRuntimeFromClient<TDescriptor>(client: PolkadotCl
|
|
|
202
276
|
* @param address - The SS58 address of the account to map.
|
|
203
277
|
* @param signer - A signer matching `address`.
|
|
204
278
|
* @param options - Optional timeout / status callback (forwarded to the underlying tx).
|
|
205
|
-
* @returns
|
|
279
|
+
* @returns A `Result`: `ok(TxResult)` from the mapping extrinsic, `ok(null)` if already mapped,
|
|
280
|
+
* or `err(TxError)` on failure. Delegates to `@parity/product-sdk-tx`'s `ensureAccountMapped`.
|
|
206
281
|
*
|
|
207
282
|
* @example
|
|
208
283
|
* ```ts
|
|
@@ -216,7 +291,7 @@ declare function createContractRuntimeFromClient<TDescriptor>(client: PolkadotCl
|
|
|
216
291
|
declare function ensureContractAccountMapped(runtime: ContractRuntime, address: SS58String, signer: PolkadotSigner, options?: {
|
|
217
292
|
timeoutMs?: number;
|
|
218
293
|
onStatus?: (s: string) => void;
|
|
219
|
-
}): Promise<TxResult | null
|
|
294
|
+
}): Promise<Result<TxResult | null, TxError>>;
|
|
220
295
|
|
|
221
296
|
type CdmJsonDependencyVersion = number | string;
|
|
222
297
|
/**
|
|
@@ -402,9 +477,13 @@ type Contract<C extends ContractDef> = {
|
|
|
402
477
|
* Resolves at best-block by default (configurable via `waitFor`).
|
|
403
478
|
*
|
|
404
479
|
* Signer is resolved from: explicit `{ signer }` option → signerManager →
|
|
405
|
-
* defaultSigner.
|
|
480
|
+
* defaultSigner.
|
|
481
|
+
*
|
|
482
|
+
* @returns A {@link Result}: `ok(TxResult)` once included/finalized, or `err`
|
|
483
|
+
* carrying a `ContractError` (no signer available, pre-submit dry-run failed,
|
|
484
|
+
* or the call reverted) or a `TxError` (submission/dispatch failure).
|
|
406
485
|
*/
|
|
407
|
-
tx: (...args: [...C["methods"][K]["args"], opts?: TxOptions]) => Promise<TxResult
|
|
486
|
+
tx: (...args: [...C["methods"][K]["args"], opts?: TxOptions]) => Promise<Result<TxResult, ContractError | TxError>>;
|
|
408
487
|
/**
|
|
409
488
|
* Prepare the method as a {@link BatchableCall} — returns the same
|
|
410
489
|
* submittable that `.tx()` would build, but without signing or
|
|
@@ -423,14 +502,17 @@ type Contract<C extends ContractDef> = {
|
|
|
423
502
|
* omitted, `.prepare()` runs a `ReviveApi.call` dry-run (same as
|
|
424
503
|
* `.tx()`) against the fallback origin to fill the missing
|
|
425
504
|
* field(s) — pass both to skip the dry-run entirely. A failing
|
|
426
|
-
* dry-run
|
|
505
|
+
* dry-run yields `err({@link ContractDryRunFailedError})` before the
|
|
427
506
|
* call is constructed.
|
|
428
507
|
*
|
|
429
508
|
* `.prepare()` does not require a signer; the batch's signer
|
|
430
509
|
* replaces the dispatched origin at submission.
|
|
510
|
+
*
|
|
511
|
+
* @returns A {@link Result}: `ok(BatchableCall)`, or `err(ContractError)` if the
|
|
512
|
+
* pre-build dry-run fails or the call reverts.
|
|
431
513
|
*/
|
|
432
|
-
prepare: (...args: [...C["methods"][K]["args"], opts?: PrepareOptions]) => Promise<BatchableCall
|
|
514
|
+
prepare: (...args: [...C["methods"][K]["args"], opts?: PrepareOptions]) => Promise<Result<BatchableCall, ContractError>>;
|
|
433
515
|
};
|
|
434
516
|
};
|
|
435
517
|
|
|
436
|
-
export { type AbiEntry as A, type CdmJson as C, type LiveContractResolutionOptions as L, type PrepareOptions as P, type QueryOptions as Q, type ReviveDryRunCall as R, type TxOptions as T, type ContractRuntime as a, type ContractManagerOptions as b, type ContractDefaults as c, type ContractRuntimeOptions as d, type Contracts as
|
|
518
|
+
export { type AbiEntry as A, type CdmJson as C, type DecodedContractRevert as D, type LiveContractResolutionOptions as L, type PrepareOptions as P, type QueryOptions as Q, type ReviveDryRunCall as R, type TxOptions as T, type ContractRuntime as a, type ContractManagerOptions as b, type ContractDefaults as c, type ContractRuntimeOptions as d, ContractError as e, type Contracts as f, type Contract as g, type ContractDef as h, type ContractOptions as i, type AbiParam as j, type CdmJsonContract as k, type CdmJsonDependencyVersion as l, type ContractDryRunAt as m, ContractDryRunFailedError as n, ContractLiveAddressResolutionError as o, ContractNotFoundError as p, type ContractRevertInfo as q, ContractRevertedError as r, ContractSignerMissingError as s, type QueryResult as t, type ReviveDryRunCallOptions as u, type ReviveDryRunResult as v, type ReviveTypedApi as w, createContractRuntime as x, createContractRuntimeFromClient as y, ensureContractAccountMapped as z };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parity/product-sdk-contracts",
|
|
3
3
|
"description": "Typed contract interactions on Polkadot Asset Hub",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.9.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
"./pvm": {
|
|
18
18
|
"types": "./dist/pvm.d.ts",
|
|
19
19
|
"import": "./dist/pvm.js"
|
|
20
|
+
},
|
|
21
|
+
"./testing": {
|
|
22
|
+
"types": "./dist/testing.d.ts",
|
|
23
|
+
"import": "./dist/testing.js"
|
|
20
24
|
}
|
|
21
25
|
},
|
|
22
26
|
"files": [
|
|
@@ -30,11 +34,13 @@
|
|
|
30
34
|
"@polkadot-labs/hdkd-helpers": "^0.0.30",
|
|
31
35
|
"polkadot-api": "^2.1.6",
|
|
32
36
|
"viem": "^2.52.0",
|
|
33
|
-
"@parity/product-sdk-signer": "0.9.0",
|
|
34
|
-
"@parity/product-sdk-keys": "0.3.13",
|
|
35
37
|
"@parity/product-sdk-address": "0.1.1",
|
|
36
38
|
"@parity/product-sdk-logger": "0.1.1",
|
|
37
|
-
"@parity/product-sdk-
|
|
39
|
+
"@parity/product-sdk-errors": "0.2.0",
|
|
40
|
+
"@parity/result": "0.2.0",
|
|
41
|
+
"@parity/product-sdk-keys": "0.3.15",
|
|
42
|
+
"@parity/product-sdk-signer": "0.11.0",
|
|
43
|
+
"@parity/product-sdk-tx": "0.3.1"
|
|
38
44
|
},
|
|
39
45
|
"devDependencies": {
|
|
40
46
|
"tsup": "^8.5.1",
|
|
@@ -45,6 +51,7 @@
|
|
|
45
51
|
"scripts": {
|
|
46
52
|
"build": "tsup",
|
|
47
53
|
"clean": "rm -rf dist",
|
|
48
|
-
"test": "vitest run"
|
|
54
|
+
"test": "vitest run",
|
|
55
|
+
"typecheck": "tsc -p tsconfig.typecheck.json"
|
|
49
56
|
}
|
|
50
57
|
}
|