Skip to content

Commit db250b0

Browse files
brunopgalvaoCopilotnhussein11eshaben
authored
Add content around working with non-sufficient assets (#774)
* add sufficient and non-sufficient guide for AH * python3 scripts/generate_llms.py * re-structure moving theory to separate section from tutorials * python3 scripts/generate_llms.py * AH natively supports paying XCM fees with a sufficient asset * refactor and add batch tutorial * improve structure and content * using an updated code snippet instead * update with Paseo TestNet instead of Westend * formatting content, add dry-run code * Update tutorials/polkadot-sdk/system-chains/index.md Co-authored-by: Copilot <[email protected]> * Update .snippets/code/develop/interoperability/best-practices-for-teleporting-assets/dry-run-example.ts Co-authored-by: Nicolás Hussein <[email protected]> * Update .snippets/code/develop/interoperability/best-practices-for-teleporting-assets/dry-run-example.ts Co-authored-by: Nicolás Hussein <[email protected]> * Update .snippets/code/develop/interoperability/best-practices-for-teleporting-assets/dry-run-example.ts Co-authored-by: Nicolás Hussein <[email protected]> * Update .snippets/code/tutorials/polkadot-sdk/system-chains/asset-hub/batch-teleport-with-dry-run.ts Co-authored-by: Nicolás Hussein <[email protected]> * Update .snippets/code/tutorials/polkadot-sdk/system-chains/asset-hub/batch-teleport-with-dry-run.ts Co-authored-by: Nicolás Hussein <[email protected]> * Update .snippets/code/tutorials/polkadot-sdk/system-chains/asset-hub/batch-teleport-with-dry-run.ts Co-authored-by: Nicolás Hussein <[email protected]> * Update develop/interoperability/best-practices-for-teleporting-assets.md Co-authored-by: Nicolás Hussein <[email protected]> * Update develop/interoperability/best-practices-for-teleporting-assets.md Co-authored-by: Nicolás Hussein <[email protected]> * Update develop/interoperability/best-practices-for-teleporting-assets.md Co-authored-by: Nicolás Hussein <[email protected]> * Update develop/interoperability/best-practices-for-teleporting-assets.md Co-authored-by: Nicolás Hussein <[email protected]> * Update develop/interoperability/best-practices-for-teleporting-assets.md Co-authored-by: Nicolás Hussein <[email protected]> * Update develop/interoperability/best-practices-for-teleporting-assets.md Co-authored-by: Nicolás Hussein <[email protected]> * Update develop/interoperability/best-practices-for-teleporting-assets.md Co-authored-by: Nicolás Hussein <[email protected]> * Update develop/interoperability/best-practices-for-teleporting-assets.md Co-authored-by: Nicolás Hussein <[email protected]> * Update develop/interoperability/best-practices-for-teleporting-assets.md Co-authored-by: Nicolás Hussein <[email protected]> * Update develop/interoperability/best-practices-for-teleporting-assets.md Co-authored-by: Nicolás Hussein <[email protected]> * Update develop/interoperability/best-practices-for-teleporting-assets.md Co-authored-by: Nicolás Hussein <[email protected]> * python3 scripts/generate_llms.py * Use You instead of We * refactor according to feedback * formatting * formatting * refactored according to feedback * refactored according to feedback * Update tutorials/polkadot-sdk/system-chains/index.md Co-authored-by: Nicolás Hussein <[email protected]> * Update tutorials/polkadot-sdk/system-chains/asset-hub/batch-teleport-assets.md Co-authored-by: Nicolás Hussein <[email protected]> * python3 scripts/generate_llms.py * Update develop/interoperability/best-practices-for-teleporting-assets.md Co-authored-by: Erin Shaben <[email protected]> * Update develop/interoperability/best-practices-for-teleporting-assets.md Co-authored-by: Erin Shaben <[email protected]> * Update tutorials/polkadot-sdk/system-chains/asset-hub/batch-teleport-assets.md Co-authored-by: Erin Shaben <[email protected]> * Update tutorials/polkadot-sdk/system-chains/asset-hub/batch-teleport-assets.md Co-authored-by: Erin Shaben <[email protected]> * Update tutorials/polkadot-sdk/system-chains/asset-hub/index.md Co-authored-by: Erin Shaben <[email protected]> * Update develop/interoperability/best-practices-for-teleporting-assets.md Co-authored-by: Erin Shaben <[email protected]> * Update develop/interoperability/best-practices-for-teleporting-assets.md Co-authored-by: Erin Shaben <[email protected]> * Update develop/interoperability/best-practices-for-teleporting-assets.md Co-authored-by: Erin Shaben <[email protected]> * Update develop/interoperability/best-practices-for-teleporting-assets.md Co-authored-by: Erin Shaben <[email protected]> * Update develop/interoperability/best-practices-for-teleporting-assets.md Co-authored-by: Erin Shaben <[email protected]> * Update polkadot-protocol/architecture/system-chains/asset-hub.md Co-authored-by: Erin Shaben <[email protected]> * Update polkadot-protocol/architecture/system-chains/asset-hub.md Co-authored-by: Erin Shaben <[email protected]> * Update tutorials/polkadot-sdk/system-chains/asset-hub/batch-teleport-assets.md Co-authored-by: Erin Shaben <[email protected]> * Update tutorials/polkadot-sdk/system-chains/asset-hub/batch-teleport-assets.md Co-authored-by: Erin Shaben <[email protected]> * Update tutorials/polkadot-sdk/system-chains/asset-hub/batch-teleport-assets.md Co-authored-by: Erin Shaben <[email protected]> * ran prettier * refactored according to feedback * refactored * Update develop/interoperability/best-practices-for-teleporting-assets.md Co-authored-by: Erin Shaben <[email protected]> * Updated content * Update polkadot-protocol/architecture/system-chains/asset-hub.md Co-authored-by: Erin Shaben <[email protected]> * Update develop/interoperability/best-practices-for-teleporting-assets.md Co-authored-by: Erin Shaben <[email protected]> * Update content * Update content --------- Co-authored-by: Copilot <[email protected]> Co-authored-by: Nicolás Hussein <[email protected]> Co-authored-by: Erin Shaben <[email protected]>
1 parent d9b4653 commit db250b0

File tree

20 files changed

+1773
-0
lines changed

20 files changed

+1773
-0
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const { ApiPromise, WsProvider } = require('@polkadot/api');
2+
const provider = new WsProvider('wss://rpc.polkadot.io');
3+
const api = await ApiPromise.create({ provider });
4+
5+
async function checkAccountExistence(api, address) {
6+
const accountInfo = await api.query.system.account(address);
7+
const balance = accountInfo.data.free.toBigInt();
8+
const existentialDeposit = api.consts.balances.existentialDeposit.toBigInt();
9+
10+
return {
11+
exists: !accountInfo.isEmpty,
12+
balance,
13+
hasExistentialDeposit: balance >= existentialDeposit,
14+
};
15+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { dot } from '@polkadot-api/descriptors';
2+
import { createClient } from 'polkadot-api';
3+
import { getWsProvider } from 'polkadot-api/ws-provider/web';
4+
5+
async function queryAccountBalance(address: string) {
6+
const client = createClient(getWsProvider('wss://rpc.polkadot.io'));
7+
const api = client.getTypedApi(dot);
8+
9+
const accountInfo = await api.query.System.Account.getValue(address);
10+
const existentialDeposit = await api.constants.Balances.ExistentialDeposit();
11+
12+
return {
13+
balance: accountInfo?.data.free ?? 0n,
14+
hasED: (accountInfo?.data.free ?? 0n) >= existentialDeposit,
15+
};
16+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { ah } from '@polkadot-api/descriptors';
2+
import { createClient } from 'polkadot-api';
3+
import { getWsProvider } from 'polkadot-api/ws-provider/web';
4+
5+
const client = createClient(
6+
getWsProvider('wss://asset-hub-paseo.dotters.network')
7+
);
8+
const api = client.getTypedApi(ah);
9+
10+
const feeInAsset =
11+
await api.apis.AssetConversionApi.quote_price_exact_tokens_for_tokens(
12+
assetIn,
13+
assetOut,
14+
amountIn
15+
);
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
import {
2+
ah,
3+
myth,
4+
XcmV3Junction,
5+
XcmV3Junctions,
6+
XcmV3MultiassetFungibility,
7+
XcmV3WeightLimit,
8+
XcmV4AssetAssetFilter,
9+
XcmV4AssetWildAsset,
10+
XcmV4Instruction,
11+
XcmVersionedAssets,
12+
XcmVersionedLocation,
13+
XcmVersionedXcm,
14+
} from '@polkadot-api/descriptors';
15+
import {
16+
createClient,
17+
Enum,
18+
FixedSizeBinary,
19+
type Transaction,
20+
} from 'polkadot-api';
21+
import { getWsProvider } from 'polkadot-api/ws-provider/web';
22+
import { withPolkadotSdkCompat } from 'polkadot-api/polkadot-sdk-compat';
23+
24+
// Asset Hub constants.
25+
const ASSET_HUB_WS_URL = 'ws://localhost:8000';
26+
const ASSET_HUB_PARA_ID = 1000;
27+
const ASSET_HUB_ACCOUNT = '15oF4uVJwmo4TdGW7VfQxNLavjCXviqxT9S1MgbjMNHr6Sp5';
28+
const DOT_UNITS = 10_000_000_000n; // 10 decimals.
29+
const DOT_CENTS = DOT_UNITS / 100n;
30+
31+
// Mythos constants.
32+
const MYTHOS_WS_URL = 'ws://localhost:8001';
33+
const MYTHOS_PARA_ID = 3369;
34+
const MYTHOS_ACCOUNT = '0x69CF15A9393A0869A7fE535eCFe2C3CbaC127A40';
35+
const MYTH_UNITS = 1_000_000_000_000_000_000n; // 18 decimals.
36+
37+
// Connect to Mythos.
38+
const mythosClient = createClient(
39+
withPolkadotSdkCompat(getWsProvider(MYTHOS_WS_URL))
40+
);
41+
42+
// Get the typed API which lets us use descriptors.
43+
const mythApi = mythosClient.getTypedApi(myth);
44+
45+
// Build the teleport transaction.
46+
const tx = mythApi.tx.PolkadotXcm.limited_teleport_assets({
47+
dest: XcmVersionedLocation.V4({
48+
parents: 1,
49+
interior: XcmV3Junctions.X1(XcmV3Junction.Parachain(ASSET_HUB_PARA_ID)),
50+
}),
51+
beneficiary: XcmVersionedLocation.V4({
52+
parents: 0,
53+
interior: XcmV3Junctions.X1(
54+
XcmV3Junction.AccountId32({
55+
id: FixedSizeBinary.fromAccountId32(ASSET_HUB_ACCOUNT),
56+
network: undefined,
57+
})
58+
),
59+
}),
60+
assets: XcmVersionedAssets.V4([
61+
{
62+
id: { parents: 0, interior: XcmV3Junctions.Here() },
63+
fun: XcmV3MultiassetFungibility.Fungible(1n * MYTH_UNITS),
64+
},
65+
]),
66+
fee_asset_item: 0,
67+
weight_limit: XcmV3WeightLimit.Unlimited(),
68+
});
69+
70+
await dryRun(tx.decodedCall);
71+
72+
async function dryRun(call: Transaction<any, any, any, any>['decodedCall']) {
73+
// Dry run the teleport locally so you know you can for example withdraw
74+
// the necessary funds and pay for delivery fees.
75+
console.log('Dry running teleport on Mythos...');
76+
const localDryRunResult = await mythApi.apis.DryRunApi.dry_run_call(
77+
Enum('system', Enum('Signed', MYTHOS_ACCOUNT)),
78+
call
79+
);
80+
81+
// Only continue if the local dry run works.
82+
// The first condition is whether or not the runtime API was successful,
83+
// the second is whether or not the underlying dry-run was successful.
84+
if (
85+
localDryRunResult.success &&
86+
localDryRunResult.value.execution_result.success
87+
) {
88+
// You are interested in the message to Asset Hub that results from your call.
89+
// You filter for it here.
90+
const [_, messages] = localDryRunResult.value.forwarded_xcms.find(
91+
([location, _]) =>
92+
// You happen to know the latest version in Mythos is V5 because of the descriptors.
93+
location.type === 'V5' &&
94+
location.value.parents === 1 &&
95+
location.value.interior.type === 'X1' &&
96+
location.value.interior.value.type === 'Parachain' &&
97+
location.value.interior.value.value === ASSET_HUB_PARA_ID
98+
)!;
99+
// There could be multiple messages to Asset Hub, you know it's only one
100+
// so you take the first one.
101+
const messageToAh = messages[0];
102+
103+
// You connect to Asset Hub to dry run this message there.
104+
const assetHubClient = createClient(getWsProvider(ASSET_HUB_WS_URL));
105+
const ahApi = assetHubClient.getTypedApi(ah);
106+
107+
// You need to ensure it's V4 because V5 is not supported by Asset Hub at the time of writing.
108+
// You get the supported versions directly from the descriptors.
109+
if (messageToAh.type === 'V4') {
110+
console.log('Dry running on Asset Hub...');
111+
const remoteDryRunResult = await ahApi.apis.DryRunApi.dry_run_xcm(
112+
XcmVersionedLocation.V4({
113+
parents: 1,
114+
interior: XcmV3Junctions.X1(XcmV3Junction.Parachain(MYTHOS_PARA_ID)),
115+
}),
116+
messageToAh
117+
);
118+
119+
if (
120+
remoteDryRunResult.success &&
121+
remoteDryRunResult.value.execution_result.type === 'Complete'
122+
) {
123+
// Success! Let's go ahead with the teleport.
124+
console.log('Success!');
125+
} else {
126+
// It probably failed because your account doesn't exist on Asset Hub and
127+
// You don't have DOT for the existential deposit.
128+
// You should solve that problem and try again.
129+
// You will later improve errors so that you know exactly what went wrong and can act accordingly.
130+
// See https://github.com/paritytech/polkadot-sdk/issues/6119.
131+
console.log('Failure :( Going to try again.');
132+
133+
// You're manually building an XCM here since you want to use the `ExchangeAsset` instruction
134+
// to swap some of the MYTH for DOT to cover the existential deposit.
135+
// This is executed via `PolkadotXcm.execute()` on Mythos and will send a message to Asset Hub.
136+
const xcm = XcmVersionedXcm.V4([
137+
// You withdraw some MYTH from your account on Mythos.
138+
XcmV4Instruction.WithdrawAsset([
139+
{
140+
id: { parents: 0, interior: XcmV3Junctions.Here() },
141+
fun: XcmV3MultiassetFungibility.Fungible(100n * MYTH_UNITS),
142+
},
143+
]),
144+
// Use them to pay fees.
145+
XcmV4Instruction.BuyExecution({
146+
fees: {
147+
id: { parents: 0, interior: XcmV3Junctions.Here() },
148+
fun: XcmV3MultiassetFungibility.Fungible(100n * MYTH_UNITS),
149+
},
150+
weight_limit: XcmV3WeightLimit.Unlimited(),
151+
}),
152+
// Teleport the MYTH to Asset Hub.
153+
XcmV4Instruction.InitiateTeleport({
154+
assets: XcmV4AssetAssetFilter.Wild(
155+
XcmV4AssetWildAsset.AllCounted(1)
156+
),
157+
dest: {
158+
parents: 1,
159+
interior: XcmV3Junctions.X1(
160+
XcmV3Junction.Parachain(ASSET_HUB_PARA_ID)
161+
),
162+
},
163+
xcm: [
164+
// You pay fees with MYTH on Asset Hub.
165+
// This is possible because Asset Hub allows paying fees with any asset that has a liquidity pool.
166+
XcmV4Instruction.BuyExecution({
167+
fees: {
168+
id: {
169+
parents: 1,
170+
interior: XcmV3Junctions.X1(
171+
XcmV3Junction.Parachain(MYTHOS_PARA_ID)
172+
),
173+
},
174+
fun: XcmV3MultiassetFungibility.Fungible(50n * MYTH_UNITS),
175+
},
176+
weight_limit: XcmV3WeightLimit.Unlimited(),
177+
}),
178+
// You explicitly swap your MYTH for 0.01 DOT to cover ED.
179+
XcmV4Instruction.ExchangeAsset({
180+
give: XcmV4AssetAssetFilter.Wild(
181+
XcmV4AssetWildAsset.AllCounted(1)
182+
),
183+
want: [
184+
{
185+
id: { parents: 1, interior: XcmV3Junctions.Here() },
186+
fun: XcmV3MultiassetFungibility.Fungible(1n * DOT_CENTS),
187+
},
188+
],
189+
maximal: false,
190+
}),
191+
// You deposit all your MYTH and your 0.01 DOT into the beneficiary account.
192+
XcmV4Instruction.DepositAsset({
193+
assets: XcmV4AssetAssetFilter.Wild(
194+
XcmV4AssetWildAsset.AllCounted(2)
195+
),
196+
beneficiary: {
197+
parents: 0,
198+
interior: XcmV3Junctions.X1(
199+
XcmV3Junction.AccountId32({
200+
id: FixedSizeBinary.fromAccountId32(ASSET_HUB_ACCOUNT),
201+
network: undefined,
202+
})
203+
),
204+
},
205+
}),
206+
],
207+
}),
208+
]);
209+
210+
const tx = mythApi.tx.PolkadotXcm.execute({
211+
message: xcm,
212+
max_weight: { ref_time: 4_000_000_000n, proof_size: 300_000n },
213+
});
214+
215+
// You try the dry run again.
216+
dryRun(tx.decodedCall);
217+
}
218+
}
219+
}
220+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { dot } from '@polkadot-api/descriptors';
2+
import { createClient } from 'polkadot-api';
3+
import { getWsProvider } from 'polkadot-api/ws-provider/web';
4+
5+
const client = createClient(getWsProvider('wss://rpc.polkadot.io'));
6+
const api = client.getTypedApi(dot);
7+
8+
const feeDetails = await api.apis.TransactionPaymentApi.query_fee_details(
9+
call.toHex(),
10+
call.encodedLength
11+
);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { dot } from '@polkadot-api/descriptors';
2+
import { createClient } from 'polkadot-api';
3+
import { getWsProvider } from 'polkadot-api/ws-provider/web';
4+
5+
const client = createClient(getWsProvider('wss://rpc.polkadot.io'));
6+
const api = client.getTypedApi(dot);
7+
8+
const deliveryFees = await api.apis.XcmPaymentApi.query_delivery_fees(
9+
destination,
10+
message
11+
);
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { dot } from '@polkadot-api/descriptors';
2+
import { createClient } from 'polkadot-api';
3+
import { getWsProvider } from 'polkadot-api/ws-provider/web';
4+
5+
const client = createClient(getWsProvider('wss://rpc.polkadot.io'));
6+
const api = client.getTypedApi(dot);
7+
8+
const xcm = XcmVersionedXcm.V5([...]);
9+
10+
// These will be set if the runtime API calls are successful.
11+
let localExecutionFees = 0;
12+
// We query the weight of the xcm.
13+
const weightResult = await api.apis.XcmPaymentApi.query_xcm_weight(xcm);
14+
if (weightResult.success) {
15+
// We convert the weight to a fee amount.
16+
// The asset is { parents: 1, interior: Here }, aka, DOT.
17+
const executionFeesResult = await api.apis.XcmPaymentApi.query_weight_to_asset_fee(
18+
weightResult.value,
19+
XcmVersionedAssetId.V5({
20+
parents: 1,
21+
interior: XcmV3Junctions.Here(),
22+
}),
23+
);
24+
if (executionFeesResult.success) {
25+
localExecutionFees = executionFeesResult.value;
26+
}
27+
}

0 commit comments

Comments
 (0)