Skip to content

Commit 2388ee4

Browse files
committed
feat: call runtime nfts example
1 parent 0acb53a commit 2388ee4

8 files changed

Lines changed: 1378 additions & 20 deletions

File tree

Cargo.lock

Lines changed: 235 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/runtime/Cargo.toml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ frame-support = { git = "https://github.com/use-ink/polkadot-sdk.git", rev = "cb
1717
pallet-assets = { git = "https://github.com/use-ink/polkadot-sdk.git", rev = "cbab8ed4be1941420dd25dc81102fb79d8e2a7f0", default-features = false }
1818
pallet-assets-precompiles = { git = "https://github.com/use-ink/polkadot-sdk.git", rev = "cbab8ed4be1941420dd25dc81102fb79d8e2a7f0", default-features = false }
1919
pallet-balances = { git = "https://github.com/use-ink/polkadot-sdk.git", rev = "cbab8ed4be1941420dd25dc81102fb79d8e2a7f0", default-features = false }
20+
pallet-nfts = { git = "https://github.com/use-ink/polkadot-sdk.git", rev = "cbab8ed4be1941420dd25dc81102fb79d8e2a7f0", default-features = false }
21+
pallet-xcm = { git = "https://github.com/use-ink/polkadot-sdk.git", rev = "cbab8ed4be1941420dd25dc81102fb79d8e2a7f0", default-features = false }
22+
pallet-xcm-precompiles = { git = "https://github.com/use-ink/polkadot-sdk.git", rev = "cbab8ed4be1941420dd25dc81102fb79d8e2a7f0", default-features = false }
2023
pallet-transaction-payment = { git = "https://github.com/use-ink/polkadot-sdk.git", rev = "cbab8ed4be1941420dd25dc81102fb79d8e2a7f0", default-features = false }
2124
pallet-revive = { git = "https://github.com/use-ink/polkadot-sdk.git", rev = "cbab8ed4be1941420dd25dc81102fb79d8e2a7f0", default-features = false }
2225
pallet-timestamp = { git = "https://github.com/use-ink/polkadot-sdk.git", rev = "cbab8ed4be1941420dd25dc81102fb79d8e2a7f0", default-features = false }
@@ -25,6 +28,9 @@ sp-core = { workspace = true, default-features = false }
2528
sp-externalities = { git = "https://github.com/use-ink/polkadot-sdk.git", rev = "cbab8ed4be1941420dd25dc81102fb79d8e2a7f0", default-features = false }
2629
sp-runtime = { version = "43.0.0", default-features = false }
2730
sp-io = { git = "https://github.com/use-ink/polkadot-sdk.git", rev = "cbab8ed4be1941420dd25dc81102fb79d8e2a7f0", default-features = false }
31+
xcm = { package = "staging-xcm", git = "https://github.com/use-ink/polkadot-sdk.git", rev = "cbab8ed4be1941420dd25dc81102fb79d8e2a7f0", default-features = false }
32+
xcm-builder = { package = "staging-xcm-builder", git = "https://github.com/use-ink/polkadot-sdk.git", rev = "cbab8ed4be1941420dd25dc81102fb79d8e2a7f0", default-features = false }
33+
xcm-executor = { package = "staging-xcm-executor", git = "https://github.com/use-ink/polkadot-sdk.git", rev = "cbab8ed4be1941420dd25dc81102fb79d8e2a7f0", default-features = false }
2834

2935
ink_primitives = { workspace = true }
3036
ink_revive_types = { workspace = true }
@@ -56,11 +62,20 @@ std = [
5662
"pallet-transaction-payment/std",
5763
"pallet-revive/std",
5864
"pallet-timestamp/std",
65+
"pallet-nfts/std",
66+
"pallet-xcm/std",
67+
"pallet-xcm-precompiles/std",
5968
"scale/std",
6069
"scale-info/std",
6170
"sp-core/std",
6271
"sp-externalities/std",
6372
"sp-io/std",
6473
"ink_e2e_macro/std",
65-
"sp-runtime/std"
74+
"sp-runtime/std",
75+
"xcm/std",
76+
"xcm-builder/std",
77+
"xcm-executor/std",
6678
]
79+
80+
# Enable XCM + pallet-nfts in the test runtime.
81+
xcm = []

crates/runtime/src/api.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
pub mod assets_api;
22
pub mod balance_api;
3+
pub mod nfts_api;
34
pub mod revive_api;
45
pub mod system_api;
56
pub mod timestamp_api;
@@ -8,6 +9,7 @@ pub mod prelude {
89
pub use super::{
910
assets_api::AssetsAPI,
1011
balance_api::BalanceAPI,
12+
nfts_api::NftsAPI,
1113
revive_api::ContractAPI,
1214
system_api::SystemAPI,
1315
timestamp_api::TimestampAPI,

crates/runtime/src/api/nfts_api.rs

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
use crate::{
2+
AccountIdFor,
3+
IntoAccountId,
4+
RuntimeEnv,
5+
};
6+
use frame_support::{
7+
pallet_prelude::DispatchError,
8+
traits::tokens::nonfungibles_v2::{
9+
Create,
10+
Inspect,
11+
Mutate,
12+
Transfer,
13+
},
14+
};
15+
use pallet_nfts::{
16+
CollectionConfigFor,
17+
CollectionDetailsFor,
18+
ItemConfig,
19+
ItemSettings,
20+
};
21+
22+
type CollectionIdOf<T, I> = <T as pallet_nfts::Config<I>>::CollectionId;
23+
type ItemIdOf<T, I> = <T as pallet_nfts::Config<I>>::ItemId;
24+
25+
/// NFTs API for the runtime.
26+
///
27+
/// Provides helpers to create collections and manipulate items in `pallet-nfts`
28+
/// when running against the in-memory runtime backend.
29+
pub trait NftsAPI<T, I = ()>
30+
where
31+
T: RuntimeEnv,
32+
T::Runtime: pallet_nfts::Config<I>,
33+
I: 'static,
34+
{
35+
/// Creates a new collection owned by `owner` and administered by `admin`.
36+
///
37+
/// This uses the pallet's `Create` implementation directly, so the caller
38+
/// must provide a config where `CollectionSetting::DepositRequired` is not
39+
/// disabled.
40+
fn create_collection(
41+
&mut self,
42+
owner: impl IntoAccountId<AccountIdFor<T::Runtime>>,
43+
admin: impl IntoAccountId<AccountIdFor<T::Runtime>>,
44+
config: CollectionConfigFor<T::Runtime, I>,
45+
) -> Result<CollectionIdOf<T::Runtime, I>, DispatchError>;
46+
47+
/// Mints an item into `beneficiary`.
48+
///
49+
/// `deposit_to_collection_owner` controls whether the deposit is taken from
50+
/// the collection owner instead of the beneficiary.
51+
fn mint_into(
52+
&mut self,
53+
collection: &CollectionIdOf<T::Runtime, I>,
54+
item: ItemIdOf<T::Runtime, I>,
55+
beneficiary: impl IntoAccountId<AccountIdFor<T::Runtime>>,
56+
deposit_to_collection_owner: bool,
57+
) -> Result<(), DispatchError>;
58+
59+
/// Transfers ownership of an item.
60+
fn transfer(
61+
&mut self,
62+
collection: &CollectionIdOf<T::Runtime, I>,
63+
item: ItemIdOf<T::Runtime, I>,
64+
destination: impl IntoAccountId<AccountIdFor<T::Runtime>>,
65+
) -> Result<(), DispatchError>;
66+
67+
/// Burns an item, optionally enforcing ownership.
68+
fn burn(
69+
&mut self,
70+
collection: &CollectionIdOf<T::Runtime, I>,
71+
item: ItemIdOf<T::Runtime, I>,
72+
maybe_check_owner: Option<impl IntoAccountId<AccountIdFor<T::Runtime>>>,
73+
) -> Result<(), DispatchError>;
74+
75+
/// Returns the owner of an item, if any.
76+
fn owner_of(
77+
&mut self,
78+
collection: &CollectionIdOf<T::Runtime, I>,
79+
item: &ItemIdOf<T::Runtime, I>,
80+
) -> Option<AccountIdFor<T::Runtime>>;
81+
82+
/// Returns collection details, if the collection exists.
83+
fn collection_details(
84+
&mut self,
85+
collection: &CollectionIdOf<T::Runtime, I>,
86+
) -> Option<CollectionDetailsFor<T::Runtime, I>>;
87+
88+
/// Returns the config for a collection, if it exists.
89+
fn collection_config(
90+
&mut self,
91+
collection: &CollectionIdOf<T::Runtime, I>,
92+
) -> Option<CollectionConfigFor<T::Runtime, I>>;
93+
94+
/// Returns the next collection ID.
95+
fn next_collection_id(&mut self) -> Option<CollectionIdOf<T::Runtime, I>>;
96+
97+
/// Checks if a collection exists.
98+
fn collection_exists(&mut self, collection: &CollectionIdOf<T::Runtime, I>) -> bool;
99+
100+
/// Checks if an item exists inside a collection.
101+
fn item_exists(
102+
&mut self,
103+
collection: &CollectionIdOf<T::Runtime, I>,
104+
item: &ItemIdOf<T::Runtime, I>,
105+
) -> bool;
106+
}
107+
108+
impl<T, I> NftsAPI<T, I> for T
109+
where
110+
T: RuntimeEnv,
111+
T::Runtime: pallet_nfts::Config<I>,
112+
I: 'static,
113+
{
114+
fn create_collection(
115+
&mut self,
116+
owner: impl IntoAccountId<AccountIdFor<T::Runtime>>,
117+
admin: impl IntoAccountId<AccountIdFor<T::Runtime>>,
118+
config: CollectionConfigFor<T::Runtime, I>,
119+
) -> Result<CollectionIdOf<T::Runtime, I>, DispatchError> {
120+
let owner = owner.into_account_id();
121+
let admin = admin.into_account_id();
122+
self.execute_with(|| {
123+
<pallet_nfts::Pallet<T::Runtime, I> as Create<
124+
AccountIdFor<T::Runtime>,
125+
CollectionConfigFor<T::Runtime, I>,
126+
>>::create_collection(&owner, &admin, &config)
127+
})
128+
}
129+
130+
fn mint_into(
131+
&mut self,
132+
collection: &CollectionIdOf<T::Runtime, I>,
133+
item: ItemIdOf<T::Runtime, I>,
134+
beneficiary: impl IntoAccountId<AccountIdFor<T::Runtime>>,
135+
deposit_to_collection_owner: bool,
136+
) -> Result<(), DispatchError> {
137+
let beneficiary = beneficiary.into_account_id();
138+
let item_config = ItemConfig {
139+
settings: ItemSettings::all_enabled(),
140+
};
141+
self.execute_with(|| {
142+
<pallet_nfts::Pallet<T::Runtime, I> as Mutate<
143+
AccountIdFor<T::Runtime>,
144+
ItemConfig,
145+
>>::mint_into(
146+
collection,
147+
&item,
148+
&beneficiary,
149+
&item_config,
150+
deposit_to_collection_owner,
151+
)
152+
})
153+
}
154+
155+
fn transfer(
156+
&mut self,
157+
collection: &CollectionIdOf<T::Runtime, I>,
158+
item: ItemIdOf<T::Runtime, I>,
159+
destination: impl IntoAccountId<AccountIdFor<T::Runtime>>,
160+
) -> Result<(), DispatchError> {
161+
let destination = destination.into_account_id();
162+
self.execute_with(|| {
163+
<pallet_nfts::Pallet<T::Runtime, I> as Transfer<
164+
AccountIdFor<T::Runtime>,
165+
>>::transfer(collection, &item, &destination)
166+
})
167+
}
168+
169+
fn burn(
170+
&mut self,
171+
collection: &CollectionIdOf<T::Runtime, I>,
172+
item: ItemIdOf<T::Runtime, I>,
173+
maybe_check_owner: Option<impl IntoAccountId<AccountIdFor<T::Runtime>>>,
174+
) -> Result<(), DispatchError> {
175+
let maybe_owner = maybe_check_owner.map(|owner| owner.into_account_id());
176+
self.execute_with(|| {
177+
<pallet_nfts::Pallet<T::Runtime, I> as Mutate<
178+
AccountIdFor<T::Runtime>,
179+
ItemConfig,
180+
>>::burn(collection, &item, maybe_owner.as_ref())
181+
})
182+
}
183+
184+
fn owner_of(
185+
&mut self,
186+
collection: &CollectionIdOf<T::Runtime, I>,
187+
item: &ItemIdOf<T::Runtime, I>,
188+
) -> Option<AccountIdFor<T::Runtime>> {
189+
self.execute_with(|| {
190+
<pallet_nfts::Pallet<T::Runtime, I> as Inspect<AccountIdFor<T::Runtime>>>::owner(
191+
collection, item,
192+
)
193+
})
194+
}
195+
196+
fn collection_details(
197+
&mut self,
198+
collection: &CollectionIdOf<T::Runtime, I>,
199+
) -> Option<CollectionDetailsFor<T::Runtime, I>> {
200+
self.execute_with(|| pallet_nfts::Collection::<T::Runtime, I>::get(collection))
201+
}
202+
203+
fn collection_config(
204+
&mut self,
205+
collection: &CollectionIdOf<T::Runtime, I>,
206+
) -> Option<CollectionConfigFor<T::Runtime, I>> {
207+
self.execute_with(|| {
208+
pallet_nfts::CollectionConfigOf::<T::Runtime, I>::get(collection)
209+
})
210+
}
211+
212+
fn next_collection_id(&mut self) -> Option<CollectionIdOf<T::Runtime, I>> {
213+
self.execute_with(|| pallet_nfts::NextCollectionId::<T::Runtime, I>::get())
214+
}
215+
216+
fn collection_exists(&mut self, collection: &CollectionIdOf<T::Runtime, I>) -> bool {
217+
self.execute_with(|| {
218+
pallet_nfts::Collection::<T::Runtime, I>::contains_key(collection)
219+
})
220+
}
221+
222+
fn item_exists(
223+
&mut self,
224+
collection: &CollectionIdOf<T::Runtime, I>,
225+
item: &ItemIdOf<T::Runtime, I>,
226+
) -> bool {
227+
self.execute_with(|| {
228+
pallet_nfts::Item::<T::Runtime, I>::contains_key(collection, item)
229+
})
230+
}
231+
}
232+
233+
#[cfg(test)]
234+
mod tests {
235+
use super::*;
236+
use crate::DefaultRuntime;
237+
use pallet_nfts::{
238+
CollectionConfig,
239+
CollectionSettings,
240+
MintSettings,
241+
};
242+
243+
type Runtime = <DefaultRuntime as RuntimeEnv>::Runtime;
244+
245+
fn simple_config() -> CollectionConfigFor<Runtime> {
246+
CollectionConfig {
247+
settings: CollectionSettings::all_enabled(),
248+
max_supply: None,
249+
mint_settings: MintSettings::default(),
250+
}
251+
}
252+
253+
#[test]
254+
fn create_and_mint_work() {
255+
let mut runtime = DefaultRuntime::default();
256+
let owner = DefaultRuntime::default_actor();
257+
let collection_id = runtime
258+
.create_collection(&owner, &owner, simple_config())
259+
.expect("create failed");
260+
261+
assert_eq!(collection_id, 0);
262+
assert!(runtime.collection_exists(&collection_id));
263+
264+
runtime
265+
.mint_into(&collection_id, 1u32, &owner, false)
266+
.expect("mint failed");
267+
268+
assert_eq!(runtime.owner_of(&collection_id, &1u32), Some(owner));
269+
}
270+
271+
#[test]
272+
fn transfer_and_burn_work() {
273+
let mut runtime = DefaultRuntime::default();
274+
let owner = DefaultRuntime::default_actor();
275+
let recipient = ink_e2e::bob().into_account_id();
276+
277+
let collection = runtime
278+
.create_collection(&owner, &owner, simple_config())
279+
.expect("create failed");
280+
runtime
281+
.mint_into(&collection, 7u32, &owner, false)
282+
.expect("mint failed");
283+
284+
runtime
285+
.transfer(&collection, 7u32, &recipient)
286+
.expect("transfer failed");
287+
assert_eq!(
288+
runtime.owner_of(&collection, &7u32),
289+
Some(recipient.clone())
290+
);
291+
292+
runtime
293+
.burn(&collection, 7u32, None::<&AccountIdFor<Runtime>>)
294+
.expect("burn failed");
295+
assert!(!runtime.item_exists(&collection, &7u32));
296+
}
297+
}

crates/runtime/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,12 @@ pub use {
5353
pallet_assets,
5454
pallet_assets_precompiles,
5555
pallet_balances,
56+
pallet_nfts,
5657
pallet_revive,
5758
pallet_timestamp,
5859
pallet_transaction_payment,
60+
pallet_xcm,
61+
pallet_xcm_precompiles,
5962
paste,
6063
scale,
6164
sp_core::crypto::Ss58Codec,
@@ -64,6 +67,9 @@ pub use {
6467
Extension,
6568
},
6669
sp_io::TestExternalities,
70+
xcm,
71+
xcm_builder,
72+
xcm_executor,
6773
};
6874

6975
pub use client::{

0 commit comments

Comments
 (0)