Skip to content

Commit 14c7ab5

Browse files
Update scripts
1 parent da09b72 commit 14c7ab5

File tree

3 files changed

+317
-30
lines changed

3 files changed

+317
-30
lines changed

.snippets/code/tutorials/interoperability/xcm-observability/deposit-reserve-asset-with-set-topic.ts

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,20 @@ async function main() {
5858
const aliceAddress = ss58Address(alicePublicKey);
5959

6060
const origin = Enum("system", Enum("Signed", aliceAddress));
61-
61+
const beneficiary = {
62+
parents: 0,
63+
interior: XcmV5Junctions.X1(XcmV5Junction.AccountId32({
64+
id: Binary.fromHex("0x9818ff3c27d256631065ecabf0c50e02551e5c5342b8669486c1e566fcbf847f")
65+
})),
66+
}
6267
const expectedMessageId = "0xd60225f721599cb7c6e23cdf4fab26f205e30cd7eb6b5ccf6637cdc80b2339b2";
6368

6469
const message = XcmVersionedXcm.V5([
6570
XcmV5Instruction.WithdrawAsset([
6671
{
6772
id: {
68-
interior: XcmV5Junctions.Here(),
6973
parents: 1,
74+
interior: XcmV5Junctions.Here(),
7075
},
7176
fun: XcmV3MultiassetFungibility.Fungible(1_000_000_000n),
7277
},
@@ -75,8 +80,8 @@ async function main() {
7580
XcmV5Instruction.BuyExecution({
7681
fees: {
7782
id: {
78-
interior: XcmV5Junctions.Here(),
7983
parents: 1,
84+
interior: XcmV5Junctions.Here(),
8085
},
8186
fun: XcmV3MultiassetFungibility.Fungible(500_000_000n),
8287
},
@@ -85,32 +90,23 @@ async function main() {
8590
XcmV5Instruction.DepositReserveAsset({
8691
assets: XcmV5AssetFilter.Wild(XcmV5WildAsset.All()),
8792
dest: {
88-
interior: XcmV5Junctions.X1(XcmV5Junction.Parachain(2034)),
8993
parents: 1,
94+
interior: XcmV5Junctions.X1(XcmV5Junction.Parachain(2034)),
9095
},
9196
xcm: [
9297
XcmV5Instruction.BuyExecution({
9398
fees: {
9499
id: {
95-
interior: XcmV5Junctions.Here(),
96100
parents: 1,
101+
interior: XcmV5Junctions.Here(),
97102
},
98103
fun: XcmV3MultiassetFungibility.Fungible(500_000_000n),
99104
},
100105
weight_limit: XcmV3WeightLimit.Unlimited(),
101106
}),
102107
XcmV5Instruction.DepositAsset({
103108
assets: XcmV5AssetFilter.Wild(XcmV5WildAsset.All()),
104-
beneficiary: {
105-
interior: XcmV5Junctions.X1(
106-
XcmV5Junction.AccountId32({
107-
id: Binary.fromHex(
108-
"0x9818ff3c27d256631065ecabf0c50e02551e5c5342b8669486c1e566fcbf847f",
109-
),
110-
}),
111-
),
112-
parents: 0,
113-
},
109+
beneficiary,
114110
}),
115111
],
116112
}),
Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
import {Binary, createClient, Enum} from "polkadot-api";
2+
import {withPolkadotSdkCompat} from "polkadot-api/polkadot-sdk-compat";
3+
import {getPolkadotSigner} from "polkadot-api/signer";
4+
import {getWsProvider} from "polkadot-api/ws-provider/web";
5+
import {
6+
assetHub,
7+
hydration,
8+
XcmV2MultiassetWildFungibility,
9+
XcmV3MultiassetFungibility,
10+
XcmV3WeightLimit,
11+
XcmV5AssetFilter,
12+
XcmV5Instruction,
13+
XcmV5Junction,
14+
XcmV5Junctions,
15+
XcmV5WildAsset,
16+
XcmVersionedXcm,
17+
} from "@polkadot-api/descriptors";
18+
import {sr25519CreateDerive} from "@polkadot-labs/hdkd";
19+
import {
20+
DEV_PHRASE,
21+
entropyToMiniSecret,
22+
mnemonicToEntropy,
23+
ss58Address,
24+
} from "@polkadot-labs/hdkd-helpers";
25+
26+
const XCM_VERSION = 5;
27+
28+
const toHuman = (_key: any, value: any) => {
29+
if (typeof value === "bigint") {
30+
return Number(value);
31+
}
32+
33+
if (value && typeof value === "object" && typeof value.asHex === "function") {
34+
return value.asHex();
35+
}
36+
37+
return value;
38+
};
39+
40+
async function main() {
41+
const para1Name = "Polkadot Asset Hub";
42+
const para1Client = createClient(
43+
withPolkadotSdkCompat(getWsProvider("ws://localhost:8000")),
44+
);
45+
const para1Api = para1Client.getTypedApi(assetHub);
46+
47+
const para2Name = "Hydration";
48+
const para2Client = createClient(
49+
withPolkadotSdkCompat(getWsProvider("ws://localhost:8001")),
50+
);
51+
const para2Api = para2Client.getTypedApi(hydration);
52+
53+
const entropy = mnemonicToEntropy(DEV_PHRASE);
54+
const miniSecret = entropyToMiniSecret(entropy);
55+
const derive = sr25519CreateDerive(miniSecret);
56+
const alice = derive("//Alice");
57+
const alicePublicKey = alice.publicKey;
58+
const aliceSigner = getPolkadotSigner(alicePublicKey, "Sr25519", alice.sign);
59+
const aliceAddress = ss58Address(alicePublicKey);
60+
61+
const origin = Enum("system", Enum("Signed", aliceAddress));
62+
const beneficiary = {
63+
parents: 0,
64+
interior: XcmV5Junctions.X1(XcmV5Junction.AccountId32({
65+
id: Binary.fromHex("0x9818ff3c27d256631065ecabf0c50e02551e5c5342b8669486c1e566fcbf847f")
66+
})),
67+
}
68+
const expectedMessageId = "0xd60225f721599cb7c6e23cdf4fab26f205e30cd7eb6b5ccf6637cdc80b2339b2";
69+
70+
const message = XcmVersionedXcm.V5([
71+
XcmV5Instruction.WithdrawAsset([{
72+
id: {
73+
parents: 0,
74+
interior: XcmV5Junctions.X2([
75+
XcmV5Junction.PalletInstance(50),
76+
XcmV5Junction.GeneralIndex(1984n),
77+
]),
78+
},
79+
fun: XcmV3MultiassetFungibility.Fungible(1_000_000n),
80+
}]),
81+
82+
XcmV5Instruction.SetFeesMode({jit_withdraw: true}),
83+
84+
XcmV5Instruction.DepositReserveAsset({
85+
assets: XcmV5AssetFilter.Wild(
86+
XcmV5WildAsset.AllOf({
87+
id: {
88+
parents: 0,
89+
interior: XcmV5Junctions.X2([
90+
XcmV5Junction.PalletInstance(50),
91+
XcmV5Junction.GeneralIndex(1984n),
92+
]),
93+
},
94+
fun: XcmV2MultiassetWildFungibility.Fungible(),
95+
})),
96+
dest: {
97+
parents: 1,
98+
interior: XcmV5Junctions.X1(XcmV5Junction.Parachain(2034)),
99+
},
100+
xcm: [
101+
XcmV5Instruction.BuyExecution({
102+
fees: {
103+
id: {
104+
parents: 1,
105+
interior: XcmV5Junctions.X3([
106+
XcmV5Junction.Parachain(1000),
107+
XcmV5Junction.PalletInstance(50),
108+
XcmV5Junction.GeneralIndex(1984n),
109+
]),
110+
},
111+
fun: XcmV3MultiassetFungibility.Fungible(1_000_000n),
112+
},
113+
weight_limit: XcmV3WeightLimit.Unlimited(),
114+
}),
115+
116+
XcmV5Instruction.ExchangeAsset({
117+
give: XcmV5AssetFilter.Wild(
118+
XcmV5WildAsset.AllOf({
119+
id: {
120+
parents: 1,
121+
interior: XcmV5Junctions.X3([
122+
XcmV5Junction.Parachain(1000),
123+
XcmV5Junction.PalletInstance(50),
124+
XcmV5Junction.GeneralIndex(1984n),
125+
]),
126+
},
127+
fun: XcmV2MultiassetWildFungibility.Fungible(),
128+
}),
129+
),
130+
want: [
131+
{
132+
id: {
133+
parents: 1,
134+
interior: XcmV5Junctions.Here(),
135+
},
136+
fun: XcmV3MultiassetFungibility.Fungible(2_360_180_274n),
137+
},
138+
],
139+
maximal: false,
140+
}),
141+
142+
XcmV5Instruction.InitiateReserveWithdraw({
143+
assets: XcmV5AssetFilter.Wild(
144+
XcmV5WildAsset.AllOf({
145+
id: {
146+
parents: 1,
147+
interior: XcmV5Junctions.Here(),
148+
},
149+
fun: XcmV2MultiassetWildFungibility.Fungible(),
150+
}),
151+
),
152+
reserve: {
153+
parents: 1,
154+
interior: XcmV5Junctions.X1(
155+
XcmV5Junction.Parachain(1000),
156+
),
157+
},
158+
xcm: [
159+
XcmV5Instruction.BuyExecution({
160+
fees: {
161+
id: {
162+
parents: 1,
163+
interior: XcmV5Junctions.Here(),
164+
},
165+
fun: XcmV3MultiassetFungibility.Fungible(2_360_180_272n),
166+
},
167+
weight_limit: XcmV3WeightLimit.Unlimited(),
168+
}),
169+
170+
XcmV5Instruction.DepositAsset({
171+
assets: XcmV5AssetFilter.Wild(
172+
XcmV5WildAsset.AllOf({
173+
id: {
174+
parents: 1,
175+
interior: XcmV5Junctions.Here(),
176+
},
177+
fun: XcmV2MultiassetWildFungibility.Fungible(),
178+
}),
179+
),
180+
beneficiary,
181+
}),
182+
],
183+
}),
184+
],
185+
}),
186+
187+
XcmV5Instruction.SetTopic(Binary.fromHex(expectedMessageId)),
188+
]);
189+
190+
const weight: any =
191+
await para1Api.apis.XcmPaymentApi.query_xcm_weight(message);
192+
if (weight.success !== true) {
193+
console.error("❌ Failed to query XCM weight:", weight.error);
194+
para1Client.destroy();
195+
return;
196+
}
197+
198+
const tx: any = para1Api.tx.PolkadotXcm.execute({
199+
message,
200+
max_weight: weight.value,
201+
});
202+
const decodedCall = tx.decodedCall as any;
203+
console.log("👀 Executing XCM:", JSON.stringify(decodedCall, toHuman, 2));
204+
205+
try {
206+
const dryRunResult: any = await para1Api.apis.DryRunApi.dry_run_call(
207+
origin,
208+
decodedCall,
209+
XCM_VERSION,
210+
);
211+
console.log("📦 Dry run result:", JSON.stringify(dryRunResult.value, toHuman, 2));
212+
213+
const executionResult = dryRunResult.value.execution_result;
214+
if (!dryRunResult.success || !executionResult.success) {
215+
console.error("❌ Local dry run failed!");
216+
} else {
217+
console.log("✅ Local dry run successful.");
218+
219+
const emittedEvents: [any] = dryRunResult.value.emitted_events;
220+
const polkadotXcmSentEvent = emittedEvents.find(event =>
221+
event.type === "PolkadotXcm" && event.value.type === "Sent"
222+
);
223+
if (polkadotXcmSentEvent === undefined) {
224+
console.log(`⚠️ PolkadotXcm.Sent is available in runtimes built from stable2503-5 or later.`);
225+
} else {
226+
let parachainBlockBefore = await para2Client.getFinalizedBlock();
227+
const extrinsic = await tx.signAndSubmit(aliceSigner);
228+
const block = extrinsic.block;
229+
console.log(`📦 Finalised on ${para1Name} in block #${block.number}: ${block.hash}`);
230+
231+
if (!extrinsic.ok) {
232+
const dispatchError = extrinsic.dispatchError;
233+
if (dispatchError.type === "Module") {
234+
const modErr: any = dispatchError.value;
235+
console.error(`❌ Dispatch error in module: ${modErr.type}${modErr.value?.type}`);
236+
} else {
237+
console.error("❌ Dispatch error:", JSON.stringify(dispatchError, toHuman, 2));
238+
}
239+
}
240+
241+
const sentEvents = await para1Api.event.PolkadotXcm.Sent.pull();
242+
if (sentEvents.length > 0) {
243+
const sentMessageId = sentEvents[0].payload.message_id.asHex();
244+
console.log(`📣 Last message Sent on ${para1Name}: ${sentMessageId}`);
245+
if (sentMessageId === expectedMessageId) {
246+
console.log("✅ Sent message ID matched.");
247+
} else {
248+
console.error("❌ Sent message ID does not match expexted message ID.");
249+
}
250+
251+
let processedMessageId = undefined;
252+
const maxRetries = 8;
253+
for (let i = 0; i < maxRetries; i++) {
254+
const parachainBlockAfter = await para2Client.getFinalizedBlock();
255+
if (parachainBlockAfter.number == parachainBlockBefore.number) {
256+
const waiting = 1_000 * (2 ** i);
257+
console.log(`⏳ Waiting ${waiting}ms for ${para2Name} block to be finalised (${i + 1}/${maxRetries})...`);
258+
await new Promise((resolve) => setTimeout(resolve, waiting));
259+
continue;
260+
}
261+
262+
console.log(`📦 Finalised on ${para2Name} in block #${parachainBlockAfter.number}: ${parachainBlockAfter.hash}`);
263+
const processedEvents = await para2Api.event.MessageQueue.Processed.pull();
264+
const processingFailedEvents = await para2Api.event.MessageQueue.ProcessingFailed.pull();
265+
if (processedEvents.length > 0) {
266+
processedMessageId = processedEvents[0].payload.id.asHex();
267+
console.log(`📣 Last message Processed on ${para2Name}: ${processedMessageId}`);
268+
break;
269+
} else if (processingFailedEvents.length > 0) {
270+
processedMessageId = processingFailedEvents[0].payload.id.asHex();
271+
console.log(`📣 Last message ProcessingFailed on ${para2Name}: ${processedMessageId}`);
272+
break;
273+
} else {
274+
console.log(`📣 No Processed events on ${para2Name} found.`);
275+
parachainBlockBefore = parachainBlockAfter; // Update the block before to the latest one
276+
}
277+
}
278+
279+
if (processedMessageId === expectedMessageId) {
280+
console.log("✅ Processed Message ID matched.");
281+
} else {
282+
console.error("❌ Processed Message ID does not match expected Message ID.");
283+
}
284+
} else {
285+
console.log(`📣 No Sent events on ${para1Name} found.`);
286+
}
287+
}
288+
}
289+
} finally {
290+
para1Client.destroy();
291+
para2Client.destroy();
292+
}
293+
}
294+
295+
main().catch(console.error);

0 commit comments

Comments
 (0)