5

When trying to transfer a parachain's native token to an asset on Statemint, i am having issues in figuring out the conversion of the asset's MultiLocation = (Here, 1_000_000_000).into() into the MultiLocation of the assets pallet X2(PalletInstance(1), GeneralIndex(1)) on statemint. That is, provided that the Assets pallet is configured in the statemint parachain and asset ID 1 is already created.

I m using xcm-simulator with Statemint's xcm-configs copied into the simulated parachain:

/// Means for transacting the native currency on this chain.
pub type CurrencyTransactor = XcmCurrencyAdapter<
    // Use this currency:
    Balances,
    // Use this currency when it is a fungible asset matching the given location or name:
    IsConcrete<DotLocation>,
    // Convert an XCM MultiLocation into a local account id:
    LocationToAccountId,
    // Our chain's account ID type (we can't get away without mentioning it explicitly):
    AccountId,
    // We don't track any teleports of `Balances`.
    (),
>;

/// Means for transacting assets besides the native currency on this chain.
pub type FungiblesTransactor = FungiblesAdapter<
    // Use this fungibles implementation:
    Assets,
    // Use this currency when it is a fungible asset matching the given location or name:
    ConvertedConcreteAssetId<
        StatemintAssetId,
        Balance,
        AsPrefixedGeneralIndex<AssetsPalletLocation, StatemintAssetId, JustTry>,
        JustTry,
    >,
    // Convert an XCM MultiLocation into a local account id:
    LocationToAccountId,
    // Our chain's account ID type (we can't get away without mentioning it explicitly):
    AccountId,
    // We only want to allow teleports of known assets. We use non-zero issuance as an indication
    // that this asset is known.
    parachains_common::impls::NonZeroIssuance<AccountId, Assets>,
    // The account to use for tracking teleports.
    CheckingAccount,
>;
/// Means for transacting assets on this chain.
pub type AssetTransactors = (CurrencyTransactor, FungiblesTransactor);

Where the AssetTransactors is set in the XcmConfig struct.

In the simulator, the parachains are configured such that:

  • Parachain 1 is the native asset parachain
  • Parachain 2 is statemint

Trying to reserve transfer asset as follows:

assert_ok!(Parachain1PalletXcm::reserve_transfer_assets(
    parachain1::Origin::signed(ALICE), // Origin
    Box::new((1, Parachain(2)).into()), // Destination
    Box::new(X1(AccountId32 { network: Any, id: ALICE.into() }).into().into()), // Beneficiary
    Box::new((Here, 1_000_000_000).into()), // Asset
    0,
));

i can see that:

ReserveAssetDeposited(MultiAssets([MultiAsset { id: Concrete(MultiLocation { parents: 1, interior: X1(Parachain(1)) }), fun: Fungible(1000000000) }]))
Asset: MultiAsset { id: Concrete(MultiLocation { parents: 1, interior: X1(Parachain(1)) }), fun: Fungible(1000000000) }

And results in:

Error FailedToTransactAsset("AssetIdConversionFailed")

Which is the expected behavior from the statemint parachain config, as the code does not specify the target pallet to which the assets are transferred. However, the construction of the message should redirect the native asset from Parachain 1 to Statemint's asset pallet, so the alternative approach i took was:

let target_multiasset: MultiAssets = vec![MultiAsset {
    id: Concrete(MultiLocation {
        parents: 0,
        interior: X2(PalletInstance(1), GeneralIndex(1)),
    }),
    fun: Fungible(amount),
}]
.into();
let local_location: MultiLocation = (0, Here).into();
let parachain_1_location_for_statemint: MultiLocation = (1, Parachain(1)).into();
assert_ok!(Parachain1PalletXcm::execute(
    parachain1::Origin::signed(ALICE),
    Box::new(VersionedXcm::V2(Xcm(vec![TransferReserveAsset {
        assets: (local, amount).into(),
        dest: (1, Parachain(2)).into(),
        xcm: Xcm(vec![
            BuyExecution {
                fees: (parachain_1_location_for_statemint, amount).into(),
                weight_limit: Limited(4)
            },
            DepositAsset {
                assets: Definite(target_multiasset),
                max_assets: 1,
                beneficiary: X1(AccountId32 { network: Any, id: ALICE.into() })
                    .into()
                    .into(),
            }
        ])
    }]))),
    1000u64, // Weight
));

Which results in a trace as follows:

DepositAsset { assets: Definite(MultiAssets([MultiAsset { id: Concrete(MultiLocation { parents: 0, interior: X2(PalletInstance(1), GeneralIndex(1)) }), fun: Fungible(1000000000) }])), max_assets: 1, beneficiary: MultiLocation { parents: 0, interior: X1(AccountId32 { network: Any, id: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }) } }
RESULT: Ok(())

In this case, neither of the FungibleTransactor nor the CurrencyTransactor are called which i imagine is caused by the fact that the asset is not recognized when received by the statemint simulated parachain.

One noteworthy observation to make here is that changing the what: &MultiAsset parameter in deposit_asset in xcm-builder's FungibleAdapter to:

let what = MultiAsset {
    id: Concrete(MultiLocation {
        parents: 0,
        interior: Junctions::X2(Junction::PalletInstance(1), Junction::GeneralIndex(1)),
    }),
    fun: Fungible(1_000_000_000),
};

In the first approach calling reserve_transfer_assets would work. But this would mean that a custom conversion on the statemint parachain is required to make such a transfer happen.

Is there a way to make this work without having to change the statemint parachain in this case?

1 Answer 1

4

I am afraid that what you are trying to achieve is currently not possible for Statemint v9230.

This recent change: https://github.com/paritytech/cumulus/pull/1318/files will make your test to fail in Statemint with a UntrastedReseveLocation error.

Until this issue is solved: https://github.com/paritytech/cumulus/issues/1317 there is not a proper way of reserve transferring a parachain's native token to Statemint.

The most similar approach would be the following:

Creating a derivative of the native asset in Statemint under pallet-assets, kind of wrappedAsset. Manually mint some assets in your sovereign account in Statemint, manually burn them from your Parachain (in case they want to keep the total issuance). Then, you will be only able of making use of InitiateReserveWithdraw from your Parachain to Statemint. It would only work if you are able to reanchor properly your native asset to the corresponding GeneralIndex(id) in Statemint's pallet-assets.

I think that even if you recognise Statemine as a reserve in your Parachain, it will be pointless. The reanchoring for TransferReserveAsset happens in the origin (aka Statemint). Instead of doing this hack, I think it is worth it to wait until Statemint allows what you want.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.