I want to transfer NFTs between a relay chain and a parachain. Does anyone have any knowledge or ideas about how to accomplish this
1 Answer
There was a PR regarding supporting non-fungibles here.
And there are also some nice examples in the XCM Simulator:
/// Scenario:
/// The relay-chain teleports an NFT to a parachain.
///
/// Asserts that the parachain accounts are updated as expected.
#[test]
fn teleport_nft() {
MockNet::reset();
Relay::execute_with(|| {
// Mint the NFT (1, 69) and give it to our "parachain#1 alias".
assert_ok!(relay_chain::Uniques::mint(
relay_chain::RuntimeOrigin::signed(ALICE),
1,
69,
child_account_account_id(1, ALICE),
));
// The parachain#1 alias of Alice is what must hold it on the Relay-chain for it to be
// withdrawable by Alice on the parachain.
assert_eq!(
relay_chain::Uniques::owner(1, 69),
Some(child_account_account_id(1, ALICE))
);
});
ParaA::execute_with(|| {
assert_ok!(parachain::ForeignUniques::force_create(
parachain::RuntimeOrigin::root(),
(Parent, GeneralIndex(1)).into(),
ALICE,
false,
));
assert_eq!(
parachain::ForeignUniques::owner((Parent, GeneralIndex(1)).into(), 69u32.into()),
None,
);
assert_eq!(parachain::Balances::reserved_balance(&ALICE), 0);
// IRL Alice would probably just execute this locally on the Relay-chain, but we can't
// easily do that here since we only send between chains.
let message = Xcm(vec![
WithdrawAsset((GeneralIndex(1), 69u32).into()),
InitiateTeleport {
assets: AllCounted(1).into(),
dest: Parachain(1).into(),
xcm: Xcm(vec![DepositAsset {
assets: AllCounted(1).into(),
beneficiary: (AccountId32 { id: ALICE.into(), network: None },).into(),
}]),
},
]);
// Send teleport
let alice = AccountId32 { id: ALICE.into(), network: None };
assert_ok!(ParachainPalletXcm::send_xcm(alice, Parent, message));
});
ParaA::execute_with(|| {
assert_eq!(
parachain::ForeignUniques::owner((Parent, GeneralIndex(1)).into(), 69u32.into()),
Some(ALICE),
);
assert_eq!(parachain::Balances::reserved_balance(&ALICE), 1000);
});
Relay::execute_with(|| {
assert_eq!(relay_chain::Uniques::owner(1, 69), None);
});
}
/// Scenario:
/// The relay-chain transfers an NFT into a parachain's sovereign account, who then mints a
/// trustless-backed-derivated locally.
///
/// Asserts that the parachain accounts are updated as expected.
#[test]
fn reserve_asset_transfer_nft() {
sp_tracing::init_for_tests();
MockNet::reset();
Relay::execute_with(|| {
assert_ok!(relay_chain::Uniques::force_create(
relay_chain::RuntimeOrigin::root(),
2,
ALICE,
false
));
assert_ok!(relay_chain::Uniques::mint(
relay_chain::RuntimeOrigin::signed(ALICE),
2,
69,
child_account_account_id(1, ALICE)
));
assert_eq!(
relay_chain::Uniques::owner(2, 69),
Some(child_account_account_id(1, ALICE))
);
});
ParaA::execute_with(|| {
assert_ok!(parachain::ForeignUniques::force_create(
parachain::RuntimeOrigin::root(),
(Parent, GeneralIndex(2)).into(),
ALICE,
false,
));
assert_eq!(
parachain::ForeignUniques::owner((Parent, GeneralIndex(2)).into(), 69u32.into()),
None,
);
assert_eq!(parachain::Balances::reserved_balance(&ALICE), 0);
let message = Xcm(vec![
WithdrawAsset((GeneralIndex(2), 69u32).into()),
DepositReserveAsset {
assets: AllCounted(1).into(),
dest: Parachain(1).into(),
xcm: Xcm(vec![DepositAsset {
assets: AllCounted(1).into(),
beneficiary: (AccountId32 { id: ALICE.into(), network: None },).into(),
}]),
},
]);
// Send transfer
let alice = AccountId32 { id: ALICE.into(), network: None };
assert_ok!(ParachainPalletXcm::send_xcm(alice, Parent, message));
});
ParaA::execute_with(|| {
log::debug!(target: "xcm-exceutor", "Hello");
assert_eq!(
parachain::ForeignUniques::owner((Parent, GeneralIndex(2)).into(), 69u32.into()),
Some(ALICE),
);
assert_eq!(parachain::Balances::reserved_balance(&ALICE), 1000);
});
Relay::execute_with(|| {
assert_eq!(relay_chain::Uniques::owner(2, 69), Some(child_account_id(1)));
});
}
There was also some discussion in the Polkadot Forum: