@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/types.ts
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core type definitions for D9 Ink SDK
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
InkCallableDescriptor,
|
|
7
|
+
InkDescriptors,
|
|
8
|
+
InkStorageDescriptor,
|
|
9
|
+
InkMetadata,
|
|
10
|
+
Event,
|
|
11
|
+
} from "@polkadot-api/ink-contracts";
|
|
12
|
+
import type { PolkadotClient, PolkadotSigner, SS58String } from "polkadot-api";
|
|
13
|
+
import type { Observable } from "rxjs";
|
|
14
|
+
import type { ContractError } from "./errors";
|
|
15
|
+
import type {
|
|
16
|
+
DecodedContractEvent,
|
|
17
|
+
EventSubscriptionOptions,
|
|
18
|
+
} from "./event-types";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Decoder type for response decoding
|
|
22
|
+
*/
|
|
23
|
+
export type ResponseDecoder = { dec: (data: Uint8Array) => unknown };
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Query options for read operations (dry-run)
|
|
27
|
+
*/
|
|
28
|
+
export interface QueryOptions<Args = unknown> {
|
|
29
|
+
/** The origin account for the call */
|
|
30
|
+
origin: SS58String;
|
|
31
|
+
/** Arguments to pass to the contract message */
|
|
32
|
+
args?: Args;
|
|
33
|
+
/** Value to transfer with the call */
|
|
34
|
+
value?: bigint;
|
|
35
|
+
/** AbortSignal for cancellation */
|
|
36
|
+
signal?: AbortSignal;
|
|
37
|
+
/** Timeout in milliseconds */
|
|
38
|
+
timeout?: number;
|
|
39
|
+
/** Block hash to query at (default: finalized) */
|
|
40
|
+
at?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Send options for write operations
|
|
45
|
+
*/
|
|
46
|
+
export interface SendOptions<Args = unknown> {
|
|
47
|
+
/** The origin account for the call */
|
|
48
|
+
origin: SS58String;
|
|
49
|
+
/** Arguments to pass to the contract message */
|
|
50
|
+
args?: Args;
|
|
51
|
+
/** Value to transfer with the call */
|
|
52
|
+
value?: bigint;
|
|
53
|
+
/** Gas limit (optional, will be estimated if not provided) */
|
|
54
|
+
gasLimit?: { refTime: bigint; proofSize: bigint };
|
|
55
|
+
/** Storage deposit limit */
|
|
56
|
+
storageDepositLimit?: bigint;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Sendable transaction that can be signed and submitted
|
|
61
|
+
*/
|
|
62
|
+
export interface SendableTransaction<T> {
|
|
63
|
+
/** Sign and submit the transaction */
|
|
64
|
+
signAndSubmit(signer: PolkadotSigner): Promise<TxResult<T>>;
|
|
65
|
+
/** Get the encoded call data */
|
|
66
|
+
getEncodedData(): Uint8Array;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Transaction result
|
|
71
|
+
*/
|
|
72
|
+
export interface TxResult<T> {
|
|
73
|
+
/** Whether the transaction was successful */
|
|
74
|
+
ok: boolean;
|
|
75
|
+
/** Transaction hash */
|
|
76
|
+
txHash: string;
|
|
77
|
+
/** Block information where tx was included */
|
|
78
|
+
block: {
|
|
79
|
+
hash: string;
|
|
80
|
+
number: number;
|
|
81
|
+
};
|
|
82
|
+
/** Decoded return value (if any) */
|
|
83
|
+
result?: T;
|
|
84
|
+
/** Events emitted by the transaction */
|
|
85
|
+
events: unknown[];
|
|
86
|
+
/** Dispatch error if failed */
|
|
87
|
+
dispatchError?: unknown;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Successful query result value
|
|
92
|
+
*/
|
|
93
|
+
export interface QuerySuccessValue<T> {
|
|
94
|
+
/** The decoded response from the contract */
|
|
95
|
+
value: T;
|
|
96
|
+
/** Events that would be emitted */
|
|
97
|
+
events: unknown[];
|
|
98
|
+
/** Gas consumed during dry-run */
|
|
99
|
+
gasConsumed: { refTime: bigint; proofSize: bigint };
|
|
100
|
+
/** Gas required for execution */
|
|
101
|
+
gasRequired: { refTime: bigint; proofSize: bigint };
|
|
102
|
+
/** Storage deposit required */
|
|
103
|
+
storageDeposit: bigint;
|
|
104
|
+
/** Create a sendable transaction from this query result */
|
|
105
|
+
send(): SendableTransaction<T>;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Query result - discriminated union for success/failure
|
|
110
|
+
* On success: QuerySuccessValue fields are spread to the same level as success
|
|
111
|
+
* On failure: error field contains the ContractError
|
|
112
|
+
*/
|
|
113
|
+
export type QueryResult<T> =
|
|
114
|
+
| ({ success: true } & QuerySuccessValue<T>)
|
|
115
|
+
| { success: false; error: ContractError };
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Storage query interface
|
|
119
|
+
*/
|
|
120
|
+
export interface ContractStorage {
|
|
121
|
+
/** Get the root storage value */
|
|
122
|
+
getRoot(): Promise<
|
|
123
|
+
{ success: true; value: unknown } | { success: false; value: ContractError }
|
|
124
|
+
>;
|
|
125
|
+
/** Get a nested storage value by path */
|
|
126
|
+
getNested(
|
|
127
|
+
path: string,
|
|
128
|
+
...keys: unknown[]
|
|
129
|
+
): Promise<
|
|
130
|
+
{ success: true; value: unknown } | { success: false; value: ContractError }
|
|
131
|
+
>;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Type helper to extract message args type
|
|
136
|
+
*/
|
|
137
|
+
type MessageArgs<
|
|
138
|
+
M extends InkCallableDescriptor,
|
|
139
|
+
K extends keyof M,
|
|
140
|
+
> = M[K]["message"];
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* ink! LangError type pattern
|
|
144
|
+
* papi generates LangError variants with { type: string; value?: unknown }
|
|
145
|
+
*/
|
|
146
|
+
type InkLangError = { type: string; value?: unknown };
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* ink! MessageResult type pattern (Result<T, LangError>)
|
|
150
|
+
* papi generates this as { success: true; value: T } | { success: false; value: LangError }
|
|
151
|
+
*/
|
|
152
|
+
type InkMessageResult<T> =
|
|
153
|
+
| { success: true; value: T }
|
|
154
|
+
| { success: false; value: unknown };
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Type helper to unwrap ink! MessageResult (Result<T, LangError>)
|
|
158
|
+
* 1. Extract T from { success: true; value: T } | { success: false; value: ... }
|
|
159
|
+
* 2. Exclude LangError variants from T
|
|
160
|
+
*/
|
|
161
|
+
type UnwrapMessageResult<T> = T extends InkMessageResult<infer U>
|
|
162
|
+
? Exclude<U, InkLangError>
|
|
163
|
+
: T;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Type helper to extract message response type (with MessageResult unwrapped)
|
|
167
|
+
*/
|
|
168
|
+
type MessageResponse<
|
|
169
|
+
M extends InkCallableDescriptor,
|
|
170
|
+
K extends keyof M,
|
|
171
|
+
> = UnwrapMessageResult<M[K]["response"]>;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* D9 Ink Contract interface
|
|
175
|
+
*/
|
|
176
|
+
export interface D9InkContract<M extends InkCallableDescriptor> {
|
|
177
|
+
/** Contract address */
|
|
178
|
+
readonly address: SS58String;
|
|
179
|
+
/** Contract metadata */
|
|
180
|
+
readonly metadata: InkMetadata;
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Query a contract message (dry-run)
|
|
184
|
+
* @param method - The message label (e.g., "PSP22::balance_of")
|
|
185
|
+
* @param options - Query options including origin and args
|
|
186
|
+
*/
|
|
187
|
+
query<K extends keyof M & string>(
|
|
188
|
+
method: K,
|
|
189
|
+
options: QueryOptions<MessageArgs<M, K>>,
|
|
190
|
+
): Promise<QueryResult<MessageResponse<M, K>>>;
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Create a sendable transaction for a contract message
|
|
194
|
+
* @param method - The message label (e.g., "PSP22::transfer")
|
|
195
|
+
* @param options - Send options including origin and args
|
|
196
|
+
*/
|
|
197
|
+
send<K extends keyof M & string>(
|
|
198
|
+
method: K,
|
|
199
|
+
options: SendOptions<MessageArgs<M, K>>,
|
|
200
|
+
): SendableTransaction<MessageResponse<M, K>>;
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Get storage query interface
|
|
204
|
+
*/
|
|
205
|
+
getStorage(): ContractStorage;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Filter events from a transaction result to only include this contract's events
|
|
209
|
+
*/
|
|
210
|
+
filterEvents(events: unknown[]): DecodedContractEvent[];
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Subscribe to contract events as an RxJS Observable
|
|
214
|
+
* @param options - Event subscription options (event labels to filter, etc.)
|
|
215
|
+
*/
|
|
216
|
+
subscribeToEvents(
|
|
217
|
+
options?: Omit<EventSubscriptionOptions, "contractAddress">,
|
|
218
|
+
): Observable<DecodedContractEvent>;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* D9 Ink SDK options
|
|
223
|
+
*/
|
|
224
|
+
export interface D9InkSdkOptions {
|
|
225
|
+
/** Default query options */
|
|
226
|
+
defaultQueryOptions?: Partial<QueryOptions>;
|
|
227
|
+
/** Default send options */
|
|
228
|
+
defaultSendOptions?: Partial<SendOptions>;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* D9 Ink SDK interface
|
|
233
|
+
*/
|
|
234
|
+
export interface D9InkSdk {
|
|
235
|
+
/**
|
|
236
|
+
* Get a contract instance for interacting with a deployed contract
|
|
237
|
+
* @param descriptor - The contract descriptor from @polkadot-api/descriptors
|
|
238
|
+
* @param address - The contract address (SS58 format)
|
|
239
|
+
*/
|
|
240
|
+
getContract<
|
|
241
|
+
S extends InkStorageDescriptor,
|
|
242
|
+
M extends InkCallableDescriptor,
|
|
243
|
+
C extends InkCallableDescriptor,
|
|
244
|
+
E extends Event,
|
|
245
|
+
>(
|
|
246
|
+
descriptor: InkDescriptors<S, M, C, E>,
|
|
247
|
+
address: SS58String,
|
|
248
|
+
): D9InkContract<M>;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export type D9InkContractFromDescriptor<D> = D extends InkDescriptors<infer _S, infer M, infer _C, infer _E> ? D9InkContract<M> : never;
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Internal options for contract creation
|
|
255
|
+
*/
|
|
256
|
+
export interface CreateContractOptions {
|
|
257
|
+
client: PolkadotClient;
|
|
258
|
+
typedApi?: unknown;
|
|
259
|
+
defaultQueryOptions?: Partial<QueryOptions>;
|
|
260
|
+
defaultSendOptions?: Partial<SendOptions>;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Type helper to infer the Messages type from an InkDescriptors
|
|
265
|
+
*/
|
|
266
|
+
export type InferMessages<D> =
|
|
267
|
+
D extends InkDescriptors<
|
|
268
|
+
InkStorageDescriptor,
|
|
269
|
+
infer M,
|
|
270
|
+
InkCallableDescriptor,
|
|
271
|
+
Event
|
|
272
|
+
>
|
|
273
|
+
? M
|
|
274
|
+
: never;
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event parsing and subscription tests
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, beforeAll, afterAll } from "bun:test";
|
|
5
|
+
import { ss58Decode } from "@polkadot-labs/hdkd-helpers";
|
|
6
|
+
import { contracts, d9, getMetadata } from "@polkadot-api/descriptors";
|
|
7
|
+
import { createClient } from "polkadot-api";
|
|
8
|
+
import { getWsProvider } from "polkadot-api/ws-provider";
|
|
9
|
+
import { withPolkadotSdkCompat } from "polkadot-api/polkadot-sdk-compat";
|
|
10
|
+
import { withLegacy } from "@polkadot-api/legacy-provider";
|
|
11
|
+
import {
|
|
12
|
+
ContractEventParser,
|
|
13
|
+
buildEventDecoder,
|
|
14
|
+
buildAllEventDecoders,
|
|
15
|
+
getEventSignature,
|
|
16
|
+
createD9InkSdk,
|
|
17
|
+
} from "../src";
|
|
18
|
+
|
|
19
|
+
const TEST_ADDRESSES = {
|
|
20
|
+
testUser: "zcb3U8vYqWLmd5huhyS7nMiMnqDQfWJrmjQcpMhkWmn7krH",
|
|
21
|
+
usdtContract: "uLj9DRUujbpCyK7USZY5ebGbxdtKoWvdRvGyyUsoLWDsNng",
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const ENDPOINT = process.env.TEST_ENDPOINT || "wss://mainnet.d9network.com:40300";
|
|
25
|
+
|
|
26
|
+
describe("Event Decoder Building", () => {
|
|
27
|
+
it("should build event decoder for Transfer event from USDT metadata", () => {
|
|
28
|
+
const metadata = contracts.usdt.metadata!;
|
|
29
|
+
|
|
30
|
+
// Build decoder for Transfer event
|
|
31
|
+
const decoder = buildEventDecoder(metadata, "Transfer");
|
|
32
|
+
|
|
33
|
+
expect(decoder).toBeDefined();
|
|
34
|
+
expect(typeof decoder).toBe("function");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("should build all event decoders from USDT metadata", () => {
|
|
38
|
+
const metadata = contracts.usdt.metadata!;
|
|
39
|
+
|
|
40
|
+
const decoders = buildAllEventDecoders(metadata);
|
|
41
|
+
|
|
42
|
+
expect(decoders).toBeDefined();
|
|
43
|
+
expect(decoders.size).toBeGreaterThan(0);
|
|
44
|
+
|
|
45
|
+
// Check that Transfer event decoder exists
|
|
46
|
+
expect(decoders.has("Transfer")).toBe(true);
|
|
47
|
+
expect(decoders.has("Approval")).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("should generate consistent event signatures", () => {
|
|
51
|
+
const sig1 = getEventSignature("Transfer");
|
|
52
|
+
const sig2 = getEventSignature("Transfer");
|
|
53
|
+
|
|
54
|
+
expect(sig1).toBeInstanceOf(Uint8Array);
|
|
55
|
+
expect(sig1.length).toBe(32); // blake2_256 produces 32 bytes
|
|
56
|
+
expect(sig1).toEqual(sig2); // Same label should produce same signature
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("should generate different signatures for different events", () => {
|
|
60
|
+
const transferSig = getEventSignature("Transfer");
|
|
61
|
+
const approvalSig = getEventSignature("Approval");
|
|
62
|
+
|
|
63
|
+
expect(transferSig).not.toEqual(approvalSig);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe("ContractEventParser", () => {
|
|
68
|
+
it("should create parser instance", () => {
|
|
69
|
+
const metadata = contracts.usdt.metadata!;
|
|
70
|
+
const parser = new ContractEventParser(metadata, TEST_ADDRESSES.usdtContract);
|
|
71
|
+
|
|
72
|
+
expect(parser).toBeDefined();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("should parse mock ContractEmitted event", () => {
|
|
76
|
+
const metadata = contracts.usdt.metadata!;
|
|
77
|
+
const parser = new ContractEventParser(metadata, TEST_ADDRESSES.usdtContract);
|
|
78
|
+
|
|
79
|
+
// Get contract address bytes
|
|
80
|
+
const [contractBytes] = ss58Decode(TEST_ADDRESSES.usdtContract);
|
|
81
|
+
|
|
82
|
+
// Get Transfer event signature
|
|
83
|
+
const transferSig = getEventSignature("Transfer");
|
|
84
|
+
|
|
85
|
+
// Create a mock Transfer event
|
|
86
|
+
// PSP22 Transfer event structure: { from: Option<AccountId>, to: Option<AccountId>, value: u128 }
|
|
87
|
+
// For simplicity, we'll test with minimal data
|
|
88
|
+
const mockEventData = new Uint8Array([
|
|
89
|
+
0, // from: None (Option discriminator)
|
|
90
|
+
0, // to: None
|
|
91
|
+
1,
|
|
92
|
+
0,
|
|
93
|
+
0,
|
|
94
|
+
0,
|
|
95
|
+
0,
|
|
96
|
+
0,
|
|
97
|
+
0,
|
|
98
|
+
0,
|
|
99
|
+
0,
|
|
100
|
+
0,
|
|
101
|
+
0,
|
|
102
|
+
0,
|
|
103
|
+
0,
|
|
104
|
+
0,
|
|
105
|
+
0,
|
|
106
|
+
0, // value: 1 (u128 little-endian)
|
|
107
|
+
]);
|
|
108
|
+
|
|
109
|
+
const mockChainEvent = {
|
|
110
|
+
event: {
|
|
111
|
+
type: "Contracts",
|
|
112
|
+
value: {
|
|
113
|
+
type: "ContractEmitted",
|
|
114
|
+
value: {
|
|
115
|
+
contract: contractBytes,
|
|
116
|
+
data: mockEventData,
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
topics: [transferSig],
|
|
121
|
+
blockNumber: 12345,
|
|
122
|
+
blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
123
|
+
eventIndex: 0,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const parsed = parser.parseEvent(mockChainEvent);
|
|
127
|
+
|
|
128
|
+
// Event should be parsed successfully
|
|
129
|
+
expect(parsed).toBeDefined();
|
|
130
|
+
if (parsed) {
|
|
131
|
+
expect(parsed.label).toBe("Transfer");
|
|
132
|
+
expect(parsed.data).toBeDefined();
|
|
133
|
+
expect(parsed.raw.blockNumber).toBe(12345);
|
|
134
|
+
expect(parsed.raw.contractAddress).toBe(TEST_ADDRESSES.usdtContract);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("should return null for non-Contracts events", () => {
|
|
139
|
+
const metadata = contracts.usdt.metadata!;
|
|
140
|
+
const parser = new ContractEventParser(metadata, TEST_ADDRESSES.usdtContract);
|
|
141
|
+
|
|
142
|
+
const mockEvent = {
|
|
143
|
+
event: {
|
|
144
|
+
type: "Balances", // Different pallet
|
|
145
|
+
value: {
|
|
146
|
+
type: "Transfer",
|
|
147
|
+
value: {},
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
topics: [],
|
|
151
|
+
blockNumber: 12345,
|
|
152
|
+
blockHash: "0x00",
|
|
153
|
+
eventIndex: 0,
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const parsed = parser.parseEvent(mockEvent);
|
|
157
|
+
expect(parsed).toBeNull();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("should return null for different contract address", () => {
|
|
161
|
+
const metadata = contracts.usdt.metadata!;
|
|
162
|
+
const parser = new ContractEventParser(metadata, TEST_ADDRESSES.usdtContract);
|
|
163
|
+
|
|
164
|
+
// Different contract address
|
|
165
|
+
const [differentContract] = ss58Decode(TEST_ADDRESSES.testUser);
|
|
166
|
+
const transferSig = getEventSignature("Transfer");
|
|
167
|
+
|
|
168
|
+
const mockEvent = {
|
|
169
|
+
event: {
|
|
170
|
+
type: "Contracts",
|
|
171
|
+
value: {
|
|
172
|
+
type: "ContractEmitted",
|
|
173
|
+
value: {
|
|
174
|
+
contract: differentContract, // Different contract
|
|
175
|
+
data: new Uint8Array([0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
topics: [transferSig],
|
|
180
|
+
blockNumber: 12345,
|
|
181
|
+
blockHash: "0x00",
|
|
182
|
+
eventIndex: 0,
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const parsed = parser.parseEvent(mockEvent);
|
|
186
|
+
expect(parsed).toBeNull(); // Should filter out different contract
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("should filter events by label", () => {
|
|
190
|
+
const metadata = contracts.usdt.metadata!;
|
|
191
|
+
const parser = new ContractEventParser(metadata, TEST_ADDRESSES.usdtContract);
|
|
192
|
+
|
|
193
|
+
const [contractBytes] = ss58Decode(TEST_ADDRESSES.usdtContract);
|
|
194
|
+
const transferSig = getEventSignature("Transfer");
|
|
195
|
+
|
|
196
|
+
const mockTransferEvent1 = {
|
|
197
|
+
event: {
|
|
198
|
+
type: "Contracts",
|
|
199
|
+
value: {
|
|
200
|
+
type: "ContractEmitted",
|
|
201
|
+
value: {
|
|
202
|
+
contract: contractBytes,
|
|
203
|
+
data: new Uint8Array([0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
topics: [transferSig],
|
|
208
|
+
blockNumber: 100,
|
|
209
|
+
blockHash: "0x00",
|
|
210
|
+
eventIndex: 0,
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const mockTransferEvent2 = {
|
|
214
|
+
event: {
|
|
215
|
+
type: "Contracts",
|
|
216
|
+
value: {
|
|
217
|
+
type: "ContractEmitted",
|
|
218
|
+
value: {
|
|
219
|
+
contract: contractBytes,
|
|
220
|
+
data: new Uint8Array([0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
topics: [transferSig],
|
|
225
|
+
blockNumber: 101,
|
|
226
|
+
blockHash: "0x00",
|
|
227
|
+
eventIndex: 1,
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
// Non-contract event should be filtered out
|
|
231
|
+
const mockBalancesEvent = {
|
|
232
|
+
event: {
|
|
233
|
+
type: "Balances",
|
|
234
|
+
value: {
|
|
235
|
+
type: "Transfer",
|
|
236
|
+
value: {},
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
topics: [],
|
|
240
|
+
blockNumber: 102,
|
|
241
|
+
blockHash: "0x00",
|
|
242
|
+
eventIndex: 2,
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const filtered = parser.filterEvents(
|
|
246
|
+
[mockTransferEvent1, mockTransferEvent2, mockBalancesEvent],
|
|
247
|
+
{
|
|
248
|
+
eventLabels: ["Transfer"],
|
|
249
|
+
},
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
expect(filtered.length).toBe(2);
|
|
253
|
+
expect(filtered[0]?.label).toBe("Transfer");
|
|
254
|
+
expect(filtered[1]?.label).toBe("Transfer");
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
describe.skipIf(!process.env.TEST_CHAIN_INTEGRATION)("Event Subscription Integration", () => {
|
|
259
|
+
let client: ReturnType<typeof createClient>;
|
|
260
|
+
let inkSdk: ReturnType<typeof createD9InkSdk>;
|
|
261
|
+
|
|
262
|
+
beforeAll(() => {
|
|
263
|
+
const provider = getWsProvider(ENDPOINT, {
|
|
264
|
+
innerEnhancer: withLegacy(),
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
client = createClient(withPolkadotSdkCompat(provider), {
|
|
268
|
+
async getMetadata(codeHash) {
|
|
269
|
+
const metadata = await getMetadata(codeHash);
|
|
270
|
+
return metadata ?? null;
|
|
271
|
+
},
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
const api = client.getTypedApi(d9);
|
|
275
|
+
inkSdk = createD9InkSdk(client, { typedApi: api });
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
afterAll(() => {
|
|
279
|
+
client.destroy();
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it(
|
|
283
|
+
"should have subscribeToEvents method on contract",
|
|
284
|
+
() => {
|
|
285
|
+
const usdtContract = inkSdk.getContract(contracts.usdt, TEST_ADDRESSES.usdtContract);
|
|
286
|
+
|
|
287
|
+
expect(usdtContract.subscribeToEvents).toBeDefined();
|
|
288
|
+
expect(typeof usdtContract.subscribeToEvents).toBe("function");
|
|
289
|
+
},
|
|
290
|
+
{ timeout: 10000 },
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
it(
|
|
294
|
+
"should create event subscription observable",
|
|
295
|
+
() => {
|
|
296
|
+
const usdtContract = inkSdk.getContract(contracts.usdt, TEST_ADDRESSES.usdtContract);
|
|
297
|
+
|
|
298
|
+
const api = client.getTypedApi(d9);
|
|
299
|
+
|
|
300
|
+
const subscription = usdtContract.subscribeToEvents({
|
|
301
|
+
eventLabels: ["Transfer"],
|
|
302
|
+
getEvents: async (blockHash: string) => {
|
|
303
|
+
return await api.query.System.Events.getValue({ at: blockHash });
|
|
304
|
+
},
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
expect(subscription).toBeDefined();
|
|
308
|
+
expect(typeof subscription.subscribe).toBe("function");
|
|
309
|
+
|
|
310
|
+
// Subscribe and immediately unsubscribe
|
|
311
|
+
const sub = subscription.subscribe({
|
|
312
|
+
next: (event) => {
|
|
313
|
+
console.log("Received event:", event);
|
|
314
|
+
},
|
|
315
|
+
error: (err) => {
|
|
316
|
+
console.error("Subscription error:", err);
|
|
317
|
+
},
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// Cleanup
|
|
321
|
+
sub.unsubscribe();
|
|
322
|
+
},
|
|
323
|
+
{ timeout: 10000 },
|
|
324
|
+
);
|
|
325
|
+
});
|