@d9-network/ink 0.0.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/index.cjs +10131 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +750 -0
- package/dist/index.d.mts +750 -0
- package/dist/index.mjs +10098 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +46 -0
- package/src/codec-builder.ts +573 -0
- package/src/contract.ts +571 -0
- package/src/decode.ts +176 -0
- package/src/encode.ts +128 -0
- package/src/errors.ts +220 -0
- package/src/event-types.ts +75 -0
- package/src/events.ts +258 -0
- package/src/index.ts +94 -0
- package/src/sdk.ts +116 -0
- package/src/subscriptions.ts +207 -0
- package/src/types.ts +274 -0
- package/test/events.test.ts +325 -0
- package/test/ink.test.ts +404 -0
- package/tsconfig.json +12 -0
- package/tsdown.config.ts +13 -0
package/src/decode.ts
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decoding utilities for ContractsApi_call response
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Src } from "@subsquid/scale-codec";
|
|
6
|
+
import { type Codec, u128, Tuple } from "@polkadot-api/substrate-bindings";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Gas information from contract execution
|
|
10
|
+
*/
|
|
11
|
+
export interface GasInfo {
|
|
12
|
+
/** Gas consumed during execution */
|
|
13
|
+
gasConsumed: { refTime: bigint; proofSize: bigint };
|
|
14
|
+
/** Gas required for execution */
|
|
15
|
+
gasRequired: { refTime: bigint; proofSize: bigint };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Storage deposit information
|
|
20
|
+
*/
|
|
21
|
+
export interface StorageDepositInfo {
|
|
22
|
+
/** Storage deposit type: 0 = Refund, 1 = Charge */
|
|
23
|
+
type: "Refund" | "Charge";
|
|
24
|
+
/** Amount of storage deposit */
|
|
25
|
+
amount: bigint;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Full decoded result from ContractsApi_call
|
|
30
|
+
*/
|
|
31
|
+
export interface ContractCallResult {
|
|
32
|
+
/** Gas information */
|
|
33
|
+
gas: GasInfo;
|
|
34
|
+
/** Storage deposit information */
|
|
35
|
+
storageDeposit: StorageDepositInfo;
|
|
36
|
+
/** Debug message (if any) */
|
|
37
|
+
debugMessage: string;
|
|
38
|
+
/** Whether the execution was successful */
|
|
39
|
+
success: boolean;
|
|
40
|
+
/** Execution flags */
|
|
41
|
+
flags: number;
|
|
42
|
+
/** The raw execution result bytes (still wrapped in Result<T, LangError>) */
|
|
43
|
+
data: Uint8Array;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Decode the raw ContractsApi_call response.
|
|
48
|
+
*
|
|
49
|
+
* The response format is:
|
|
50
|
+
* - gasConsumed: Weight { ref_time: Compact<u64>, proof_size: Compact<u64> }
|
|
51
|
+
* - gasRequired: Weight
|
|
52
|
+
* - storageDeposit: StorageDeposit { variant: u8, amount: u128 }
|
|
53
|
+
* - debugMessage: String
|
|
54
|
+
* - result: Result<ExecReturnValue, DispatchError>
|
|
55
|
+
* - ExecReturnValue: { flags: u32, data: Vec<u8> }
|
|
56
|
+
*
|
|
57
|
+
* @param result - The raw response bytes from state_call ContractsApi_call
|
|
58
|
+
* @returns Decoded contract call result
|
|
59
|
+
*/
|
|
60
|
+
export function decodeContractCallResult(result: Uint8Array): ContractCallResult {
|
|
61
|
+
const src = new Src(result);
|
|
62
|
+
|
|
63
|
+
// gasConsumed: Weight
|
|
64
|
+
const gasConsumedRefTime = BigInt(src.compact());
|
|
65
|
+
const gasConsumedProofSize = BigInt(src.compact());
|
|
66
|
+
|
|
67
|
+
// gasRequired: Weight
|
|
68
|
+
const gasRequiredRefTime = BigInt(src.compact());
|
|
69
|
+
const gasRequiredProofSize = BigInt(src.compact());
|
|
70
|
+
|
|
71
|
+
// storageDeposit: StorageDeposit
|
|
72
|
+
const storageDepositVariant = src.u8();
|
|
73
|
+
const storageDepositAmount = src.u128();
|
|
74
|
+
|
|
75
|
+
// debugMessage: String
|
|
76
|
+
const debugMessage = src.str();
|
|
77
|
+
|
|
78
|
+
// result: Result<ExecReturnValue, DispatchError>
|
|
79
|
+
const resultVariant = src.u8(); // 0 = Ok, 1 = Err
|
|
80
|
+
const success = resultVariant === 0;
|
|
81
|
+
|
|
82
|
+
// ExecReturnValue: { flags: u32, data: Vec<u8> }
|
|
83
|
+
const flags = src.u32();
|
|
84
|
+
const data = src.bytes(src.compactLength());
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
gas: {
|
|
88
|
+
gasConsumed: {
|
|
89
|
+
refTime: gasConsumedRefTime,
|
|
90
|
+
proofSize: gasConsumedProofSize,
|
|
91
|
+
},
|
|
92
|
+
gasRequired: {
|
|
93
|
+
refTime: gasRequiredRefTime,
|
|
94
|
+
proofSize: gasRequiredProofSize,
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
storageDeposit: {
|
|
98
|
+
type: storageDepositVariant === 0 ? "Refund" : "Charge",
|
|
99
|
+
amount: storageDepositAmount,
|
|
100
|
+
},
|
|
101
|
+
debugMessage,
|
|
102
|
+
success,
|
|
103
|
+
flags,
|
|
104
|
+
data,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Legacy function - decode and return just the exec result data.
|
|
110
|
+
* @deprecated Use decodeContractCallResult for full information
|
|
111
|
+
*/
|
|
112
|
+
export function decodeResult(result: Uint8Array): Uint8Array {
|
|
113
|
+
const decoded = decodeContractCallResult(result);
|
|
114
|
+
return decoded.data;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Unwrap the inner value from Result<T, LangError> by checking the variant byte.
|
|
119
|
+
*
|
|
120
|
+
* Ink contracts wrap their return values in Result<T, LangError>.
|
|
121
|
+
* This function handles the unwrapping and throws an error for LangError.
|
|
122
|
+
*
|
|
123
|
+
* @param data - The exec result bytes (Result<T, LangError> encoded)
|
|
124
|
+
* @returns The inner value bytes (T encoded)
|
|
125
|
+
* @throws Error if the result is Err variant (LangError)
|
|
126
|
+
*/
|
|
127
|
+
export function unwrapInkResult(data: Uint8Array): Uint8Array {
|
|
128
|
+
if (data.length === 0) {
|
|
129
|
+
throw new Error("Empty result data");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const variant = data[0];
|
|
133
|
+
|
|
134
|
+
if (variant === 0) {
|
|
135
|
+
// Ok variant - return the inner data (skip the variant byte)
|
|
136
|
+
return data.slice(1);
|
|
137
|
+
} else if (variant === 1) {
|
|
138
|
+
// Err variant - LangError
|
|
139
|
+
throw new Error("Contract call returned LangError");
|
|
140
|
+
} else {
|
|
141
|
+
throw new Error(`Unknown result variant: ${variant}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Check if the result indicates a LangError
|
|
147
|
+
*
|
|
148
|
+
* @param data - The exec result bytes
|
|
149
|
+
* @returns True if it's a LangError (Err variant)
|
|
150
|
+
*/
|
|
151
|
+
export function isLangError(data: Uint8Array): boolean {
|
|
152
|
+
return data.length > 0 && data[0] === 1;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Decode ink contract message result using a custom SCALE codec.
|
|
157
|
+
* This bypasses polkadot-api's ink decoder which has issues with LangError type.
|
|
158
|
+
*
|
|
159
|
+
* @param data - The exec result bytes (Result<T, LangError> encoded)
|
|
160
|
+
* @param codec - The SCALE codec for the inner value type T
|
|
161
|
+
* @returns The decoded value
|
|
162
|
+
*/
|
|
163
|
+
export function decodeInkValue<T>(data: Uint8Array, codec: Codec<T>): T {
|
|
164
|
+
const innerData = unwrapInkResult(data);
|
|
165
|
+
return codec.dec(innerData);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Pre-defined codecs for common types
|
|
170
|
+
*/
|
|
171
|
+
export const InkCodecs = {
|
|
172
|
+
/** u128 codec for Balance type */
|
|
173
|
+
u128,
|
|
174
|
+
/** Tuple of two u128 values, commonly used for (Balance, Balance) */
|
|
175
|
+
balancePair: Tuple(u128, u128),
|
|
176
|
+
};
|
package/src/encode.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encoding utilities for ContractsApi_call state_call
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Bytes } from "@subsquid/scale-codec";
|
|
6
|
+
import { HexSink } from "@subsquid/scale-codec";
|
|
7
|
+
import type { Binary } from "polkadot-api";
|
|
8
|
+
import { fromHex } from "polkadot-api/utils";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Encode a contract call for ContractsApi_call state_call.
|
|
12
|
+
*
|
|
13
|
+
* The encoded format matches the ContractsApi::call runtime API:
|
|
14
|
+
* - origin: AccountId (32 bytes)
|
|
15
|
+
* - dest: AccountId (32 bytes)
|
|
16
|
+
* - value: Balance (u128)
|
|
17
|
+
* - gas_limit: Option<Weight> (1 byte for None)
|
|
18
|
+
* - storage_deposit_limit: Option<Balance> (1 byte for None)
|
|
19
|
+
* - input_data: Vec<u8> (compact length + bytes)
|
|
20
|
+
*
|
|
21
|
+
* @param origin - The origin account (as Uint8Array or hex string)
|
|
22
|
+
* @param dest - The contract address (as Uint8Array or hex string)
|
|
23
|
+
* @param input - The encoded call data (selector + arguments)
|
|
24
|
+
* @param value - Optional value to transfer (default: 0)
|
|
25
|
+
* @returns Hex-encoded bytes for state_call
|
|
26
|
+
*/
|
|
27
|
+
export function encodeContractCall(
|
|
28
|
+
origin: Uint8Array | string,
|
|
29
|
+
dest: Uint8Array | string,
|
|
30
|
+
input: Binary | Uint8Array,
|
|
31
|
+
value: bigint = 0n,
|
|
32
|
+
): Bytes {
|
|
33
|
+
const sink = new HexSink();
|
|
34
|
+
|
|
35
|
+
const originBytes = typeof origin === "string" ? fromHex(origin) : origin;
|
|
36
|
+
const destBytes = typeof dest === "string" ? fromHex(dest) : dest;
|
|
37
|
+
const inputBytes = "asBytes" in input ? input.asBytes() : input;
|
|
38
|
+
|
|
39
|
+
// origin: AccountId
|
|
40
|
+
sink.bytes(originBytes);
|
|
41
|
+
// dest: AccountId
|
|
42
|
+
sink.bytes(destBytes);
|
|
43
|
+
// value: Balance (u128)
|
|
44
|
+
sink.u128(value);
|
|
45
|
+
// gas_limit: Option<Weight> - None
|
|
46
|
+
sink.u8(0);
|
|
47
|
+
// storage_deposit_limit: Option<Balance> - None
|
|
48
|
+
sink.u8(0);
|
|
49
|
+
// input_data: Vec<u8>
|
|
50
|
+
sink.compact(inputBytes.length);
|
|
51
|
+
sink.bytes(inputBytes);
|
|
52
|
+
|
|
53
|
+
return sink.toHex();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Encode a contract call using the same address for origin and dest.
|
|
58
|
+
* This is a simplified version for query operations where the origin
|
|
59
|
+
* doesn't matter much.
|
|
60
|
+
*
|
|
61
|
+
* @param address - The contract address
|
|
62
|
+
* @param input - The encoded call data
|
|
63
|
+
* @param value - Optional value to transfer
|
|
64
|
+
* @returns Hex-encoded bytes for state_call
|
|
65
|
+
*/
|
|
66
|
+
export function encodeCall(
|
|
67
|
+
address: Uint8Array | string,
|
|
68
|
+
input: Binary | Uint8Array,
|
|
69
|
+
value: bigint = 0n,
|
|
70
|
+
): Bytes {
|
|
71
|
+
return encodeContractCall(address, address, input, value);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Encode a contract call with specific gas limit and storage deposit limit.
|
|
76
|
+
*
|
|
77
|
+
* @param origin - The origin account
|
|
78
|
+
* @param dest - The contract address
|
|
79
|
+
* @param input - The encoded call data
|
|
80
|
+
* @param options - Call options including value, gas limit, storage deposit limit
|
|
81
|
+
* @returns Hex-encoded bytes for state_call
|
|
82
|
+
*/
|
|
83
|
+
export function encodeContractCallWithLimits(
|
|
84
|
+
origin: Uint8Array | string,
|
|
85
|
+
dest: Uint8Array | string,
|
|
86
|
+
input: Binary | Uint8Array,
|
|
87
|
+
options: {
|
|
88
|
+
value?: bigint;
|
|
89
|
+
gasLimit?: { refTime: bigint; proofSize: bigint };
|
|
90
|
+
storageDepositLimit?: bigint;
|
|
91
|
+
} = {},
|
|
92
|
+
): Bytes {
|
|
93
|
+
const sink = new HexSink();
|
|
94
|
+
|
|
95
|
+
const originBytes = typeof origin === "string" ? fromHex(origin) : origin;
|
|
96
|
+
const destBytes = typeof dest === "string" ? fromHex(dest) : dest;
|
|
97
|
+
const inputBytes = "asBytes" in input ? input.asBytes() : input;
|
|
98
|
+
|
|
99
|
+
// origin: AccountId
|
|
100
|
+
sink.bytes(originBytes);
|
|
101
|
+
// dest: AccountId
|
|
102
|
+
sink.bytes(destBytes);
|
|
103
|
+
// value: Balance (u128)
|
|
104
|
+
sink.u128(options.value ?? 0n);
|
|
105
|
+
|
|
106
|
+
// gas_limit: Option<Weight>
|
|
107
|
+
if (options.gasLimit) {
|
|
108
|
+
sink.u8(1); // Some
|
|
109
|
+
sink.compact(options.gasLimit.refTime);
|
|
110
|
+
sink.compact(options.gasLimit.proofSize);
|
|
111
|
+
} else {
|
|
112
|
+
sink.u8(0); // None
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// storage_deposit_limit: Option<Balance>
|
|
116
|
+
if (options.storageDepositLimit !== undefined) {
|
|
117
|
+
sink.u8(1); // Some
|
|
118
|
+
sink.u128(options.storageDepositLimit);
|
|
119
|
+
} else {
|
|
120
|
+
sink.u8(0); // None
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// input_data: Vec<u8>
|
|
124
|
+
sink.compact(inputBytes.length);
|
|
125
|
+
sink.bytes(inputBytes);
|
|
126
|
+
|
|
127
|
+
return sink.toHex();
|
|
128
|
+
}
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured error types for contract operations.
|
|
3
|
+
* Provides detailed error information for debugging and handling.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Error types for contract operations
|
|
8
|
+
*/
|
|
9
|
+
export type ContractErrorType =
|
|
10
|
+
| "METADATA_ERROR" // Missing or invalid contract metadata
|
|
11
|
+
| "ENCODE_ERROR" // Failed to encode call data
|
|
12
|
+
| "DECODE_ERROR" // Failed to decode response
|
|
13
|
+
| "NETWORK_ERROR" // Network or RPC error
|
|
14
|
+
| "CONTRACT_ERROR" // Contract returned an error
|
|
15
|
+
| "LANG_ERROR" // Ink LangError (CouldNotReadInput, etc.)
|
|
16
|
+
| "TIMEOUT_ERROR" // Request timeout
|
|
17
|
+
| "ABORTED" // Request was aborted
|
|
18
|
+
| "SIGNER_ERROR" // Signer related error
|
|
19
|
+
| "TX_ERROR"; // Transaction submission error
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Base error class for all contract-related errors
|
|
23
|
+
*/
|
|
24
|
+
export class ContractError extends Error {
|
|
25
|
+
public readonly timestamp: Date;
|
|
26
|
+
|
|
27
|
+
public override readonly cause?: Error;
|
|
28
|
+
|
|
29
|
+
constructor(
|
|
30
|
+
message: string,
|
|
31
|
+
public readonly type: ContractErrorType,
|
|
32
|
+
public readonly label?: string,
|
|
33
|
+
public readonly details?: unknown,
|
|
34
|
+
cause?: Error,
|
|
35
|
+
) {
|
|
36
|
+
super(message, { cause });
|
|
37
|
+
this.name = "ContractError";
|
|
38
|
+
this.timestamp = new Date();
|
|
39
|
+
|
|
40
|
+
// Maintains proper stack trace for where error was thrown
|
|
41
|
+
if (Error.captureStackTrace) {
|
|
42
|
+
Error.captureStackTrace(this, ContractError);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Create a formatted error message for logging
|
|
48
|
+
*/
|
|
49
|
+
toLogString(): string {
|
|
50
|
+
const parts = [
|
|
51
|
+
`[${this.type}]`,
|
|
52
|
+
this.label ? `${this.label}:` : "",
|
|
53
|
+
this.message,
|
|
54
|
+
].filter(Boolean);
|
|
55
|
+
|
|
56
|
+
if (this.details) {
|
|
57
|
+
parts.push(`Details: ${JSON.stringify(this.details)}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return parts.join(" ");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Convert to a plain object for serialization
|
|
65
|
+
*/
|
|
66
|
+
toJSON(): Record<string, unknown> {
|
|
67
|
+
return {
|
|
68
|
+
name: this.name,
|
|
69
|
+
type: this.type,
|
|
70
|
+
message: this.message,
|
|
71
|
+
label: this.label,
|
|
72
|
+
details: this.details,
|
|
73
|
+
timestamp: this.timestamp.toISOString(),
|
|
74
|
+
stack: this.stack,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Error thrown when contract metadata is missing or invalid
|
|
81
|
+
*/
|
|
82
|
+
export class MetadataError extends ContractError {
|
|
83
|
+
constructor(message: string, details?: unknown) {
|
|
84
|
+
super(message, "METADATA_ERROR", undefined, details);
|
|
85
|
+
this.name = "MetadataError";
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Error thrown when encoding call data fails
|
|
91
|
+
*/
|
|
92
|
+
export class EncodeError extends ContractError {
|
|
93
|
+
constructor(label: string, message: string, details?: unknown) {
|
|
94
|
+
super(message, "ENCODE_ERROR", label, details);
|
|
95
|
+
this.name = "EncodeError";
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Error thrown when decoding response fails
|
|
101
|
+
*/
|
|
102
|
+
export class DecodeError extends ContractError {
|
|
103
|
+
constructor(label: string, message: string, details?: unknown) {
|
|
104
|
+
super(message, "DECODE_ERROR", label, details);
|
|
105
|
+
this.name = "DecodeError";
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Error thrown for network/RPC errors
|
|
111
|
+
*/
|
|
112
|
+
export class NetworkError extends ContractError {
|
|
113
|
+
constructor(message: string, cause?: Error, details?: unknown) {
|
|
114
|
+
super(message, "NETWORK_ERROR", undefined, details, cause);
|
|
115
|
+
this.name = "NetworkError";
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Error thrown when contract returns an error result
|
|
121
|
+
*/
|
|
122
|
+
export class ContractExecutionError extends ContractError {
|
|
123
|
+
constructor(
|
|
124
|
+
label: string,
|
|
125
|
+
public readonly errorValue: unknown,
|
|
126
|
+
) {
|
|
127
|
+
super(
|
|
128
|
+
`Contract execution failed: ${JSON.stringify(errorValue)}`,
|
|
129
|
+
"CONTRACT_ERROR",
|
|
130
|
+
label,
|
|
131
|
+
errorValue,
|
|
132
|
+
);
|
|
133
|
+
this.name = "ContractExecutionError";
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Error thrown for Ink LangError
|
|
139
|
+
*/
|
|
140
|
+
export class LangError extends ContractError {
|
|
141
|
+
constructor(
|
|
142
|
+
label: string,
|
|
143
|
+
public readonly variant: number,
|
|
144
|
+
) {
|
|
145
|
+
const variantName =
|
|
146
|
+
variant === 1 ? "CouldNotReadInput" : `Unknown(${variant})`;
|
|
147
|
+
super(`Ink LangError: ${variantName}`, "LANG_ERROR", label, {
|
|
148
|
+
variant,
|
|
149
|
+
variantName,
|
|
150
|
+
});
|
|
151
|
+
this.name = "LangError";
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Error thrown when request times out
|
|
157
|
+
*/
|
|
158
|
+
export class TimeoutError extends ContractError {
|
|
159
|
+
constructor(
|
|
160
|
+
label: string,
|
|
161
|
+
public readonly timeoutMs: number,
|
|
162
|
+
) {
|
|
163
|
+
super(`Request timed out after ${timeoutMs}ms`, "TIMEOUT_ERROR", label, {
|
|
164
|
+
timeoutMs,
|
|
165
|
+
});
|
|
166
|
+
this.name = "TimeoutError";
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Error thrown when request is aborted
|
|
172
|
+
*/
|
|
173
|
+
export class AbortedError extends ContractError {
|
|
174
|
+
constructor(label: string, reason?: string) {
|
|
175
|
+
super(reason || "Request was aborted", "ABORTED", label, { reason });
|
|
176
|
+
this.name = "AbortedError";
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Error thrown for signer-related issues
|
|
182
|
+
*/
|
|
183
|
+
export class SignerError extends ContractError {
|
|
184
|
+
constructor(message: string, details?: unknown) {
|
|
185
|
+
super(message, "SIGNER_ERROR", undefined, details);
|
|
186
|
+
this.name = "SignerError";
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Error thrown when transaction submission fails
|
|
192
|
+
*/
|
|
193
|
+
export class TransactionError extends ContractError {
|
|
194
|
+
constructor(
|
|
195
|
+
label: string,
|
|
196
|
+
message: string,
|
|
197
|
+
public readonly txHash?: string,
|
|
198
|
+
details?: unknown,
|
|
199
|
+
) {
|
|
200
|
+
super(message, "TX_ERROR", label, { ...(details as object), txHash });
|
|
201
|
+
this.name = "TransactionError";
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Type guard to check if an error is a ContractError
|
|
207
|
+
*/
|
|
208
|
+
export function isContractError(error: unknown): error is ContractError {
|
|
209
|
+
return error instanceof ContractError;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Type guard to check for specific error types
|
|
214
|
+
*/
|
|
215
|
+
export function isErrorType<T extends ContractErrorType>(
|
|
216
|
+
error: unknown,
|
|
217
|
+
type: T,
|
|
218
|
+
): error is ContractError & { type: T } {
|
|
219
|
+
return isContractError(error) && error.type === type;
|
|
220
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event type definitions for ink! contracts
|
|
3
|
+
*/
|
|
4
|
+
import type { SS58String } from "polkadot-api";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Raw event data from chain
|
|
8
|
+
*/
|
|
9
|
+
export interface RawContractEvent {
|
|
10
|
+
/** Block number where event was emitted */
|
|
11
|
+
blockNumber: number;
|
|
12
|
+
/** Block hash */
|
|
13
|
+
blockHash: string;
|
|
14
|
+
/** Event index in block */
|
|
15
|
+
eventIndex: number;
|
|
16
|
+
/** Contract address that emitted the event */
|
|
17
|
+
contractAddress: SS58String;
|
|
18
|
+
/** Event data (SCALE encoded) */
|
|
19
|
+
data: Uint8Array;
|
|
20
|
+
/** Event topics (indexed fields) */
|
|
21
|
+
topics: Uint8Array[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Decoded contract event
|
|
26
|
+
*/
|
|
27
|
+
export interface DecodedContractEvent<T = unknown> {
|
|
28
|
+
/** Event label from metadata (e.g., "Transfer", "Approval") */
|
|
29
|
+
label: string;
|
|
30
|
+
/** Decoded event data */
|
|
31
|
+
data: T;
|
|
32
|
+
/** Original raw event */
|
|
33
|
+
raw: RawContractEvent;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Event filter options
|
|
38
|
+
*/
|
|
39
|
+
export interface EventFilterOptions {
|
|
40
|
+
/** Contract address to filter by */
|
|
41
|
+
contractAddress?: SS58String;
|
|
42
|
+
/** Event labels to include (e.g., ["Transfer", "Approval"]) */
|
|
43
|
+
eventLabels?: string[];
|
|
44
|
+
/** From block number (inclusive) */
|
|
45
|
+
fromBlock?: number;
|
|
46
|
+
/** To block number (inclusive) */
|
|
47
|
+
toBlock?: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Event subscription options
|
|
52
|
+
*/
|
|
53
|
+
export interface EventSubscriptionOptions {
|
|
54
|
+
/** Contract address to subscribe to */
|
|
55
|
+
contractAddress: SS58String;
|
|
56
|
+
/** Event labels to subscribe to (undefined = all events) */
|
|
57
|
+
eventLabels?: string[];
|
|
58
|
+
/** Include historical events from this block */
|
|
59
|
+
fromBlock?: number;
|
|
60
|
+
/**
|
|
61
|
+
* Function to fetch System.Events at a given block hash
|
|
62
|
+
* This is required because PolkadotClient doesn't expose typed API directly
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* const options = {
|
|
67
|
+
* contractAddress: usdtAddress,
|
|
68
|
+
* getEvents: async (blockHash) => {
|
|
69
|
+
* return await api.query.System.Events.getValue({ at: blockHash });
|
|
70
|
+
* }
|
|
71
|
+
* };
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
getEvents: (blockHash: string) => Promise<unknown[]>;
|
|
75
|
+
}
|