difftreelog
Offchain schema added
in: master
6 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3086,6 +3086,7 @@
"frame-support",
"frame-system",
"parity-scale-codec",
+ "serde",
"sp-core",
"sp-io",
"sp-runtime",
pallets/nft/Cargo.tomldiffbeforeafterboth--- a/pallets/nft/Cargo.toml
+++ b/pallets/nft/Cargo.toml
@@ -32,6 +32,10 @@
git = 'https://github.com/usetech-llc/substrate.git'
branch = 'rc4_ext_dispatch_reenabled'
version = '2.0.0-rc4'
+
+[dependencies]
+# third-party dependencies
+serde = { version = "1.0.102", features = ["derive"] }
[package]
authors = ['Substrate DevHub <https://github.com/substrate-developer-hub>']
@@ -49,6 +53,7 @@
default = ['std']
std = [
'codec/std',
+ "serde/std",
'frame-support/std',
'frame-system/std',
'sp-runtime/std',
pallets/nft/src/lib.rsdiffbeforeafterboth--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -14,6 +14,27 @@
#[cfg(test)]
mod tests;
+#[derive(Encode, Decode, Debug, Eq, Clone, PartialEq)]
+pub enum CollectionMode {
+ Invalid,
+ // custom data size
+ NFT(u32),
+ // amount
+ Fungible(u32),
+ ReFungible,
+}
+
+impl Into<u8> for CollectionMode {
+ fn into(self) -> u8{
+ match self {
+ CollectionMode::Invalid => 0,
+ CollectionMode::NFT(_) => 1,
+ CollectionMode::Fungible(_) => 2,
+ CollectionMode::ReFungible => 3,
+ }
+ }
+}
+
#[derive(Encode, Decode, Debug, Clone, PartialEq)]
pub enum AccessMode {
Normal,
@@ -21,13 +42,6 @@
}
impl Default for AccessMode { fn default() -> Self { Self::Normal } }
-#[derive(Encode, Decode, Debug, Eq, Clone, PartialEq)]
-pub enum CollectionMode {
- Invalid,
- NFT,
- Fungible,
- ReFungible,
-}
impl Default for CollectionMode { fn default() -> Self { Self::Invalid } }
#[derive(Encode, Decode, Default, Clone, PartialEq)]
@@ -49,6 +63,9 @@
pub description: Vec<u16>, // 256 include null escape char
pub token_prefix: Vec<u8>, // 16 include null escape char
pub custom_data_size: u32,
+ pub offchain_schema: Vec<u8>,
+ pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender
+ pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship
}
#[derive(Encode, Decode, Default, Clone, PartialEq)]
@@ -88,8 +105,10 @@
decl_storage! {
trait Store for Module<T: Trait> as Nft {
- // Next available collection ID
- NextCollectionID get(fn next_collection_id): u64;
+ // Private members
+ NextCollectionID: u64;
+ ItemListIndex: map hasher(blake2_128_concat) u64 => u64;
+
pub Collection get(fn collection): map hasher(identity) u64 => CollectionType<T::AccountId>;
pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;
@@ -101,7 +120,6 @@
pub NftItemList get(fn nft_item_id): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;
pub FungibleItemList get(fn fungible_item_id): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;
pub ReFungibleItemList get(fn refungible_item_id): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;
- ItemListIndex get(fn item_index): map hasher(blake2_128_concat) u64 => u64;
pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;
}
@@ -132,36 +150,19 @@
collection_name: Vec<u16>,
collection_description: Vec<u16>,
token_prefix: Vec<u8>,
- mode: u8,
- decimal_points: u32,
- custom_data_size: u32) -> DispatchResult {
+ mode: CollectionMode) -> DispatchResult {
// Anyone can create a collection
let who = ensure_signed(origin)?;
- let collection_mode: CollectionMode;
- match mode {
- 1 => collection_mode = CollectionMode::NFT,
- 2 => collection_mode = CollectionMode::Fungible,
- 3 => collection_mode = CollectionMode::ReFungible,
- _ => collection_mode = CollectionMode::Invalid
- }
-
- // check type
- ensure!((collection_mode == CollectionMode::Fungible || collection_mode == CollectionMode::NFT || collection_mode == CollectionMode::ReFungible),
- "Collection mode must be Fungible, NFT or ReFungible");
-
- // NFT checks
- if collection_mode == CollectionMode::NFT
- {
- ensure!(decimal_points == 0, "Collection in NFT mode must have zero in decimal_points parameter");
- }
+ let custom_data_size = match mode {
+ CollectionMode::NFT(size) => size,
+ _ => 0
+ };
- // Fungible checks
- if collection_mode == CollectionMode::Fungible
- {
- ensure!(custom_data_size == 0, "Collection in Fungible mode must have zero in custom_data_size parameter");
- ensure!(decimal_points != 0, "Collection in Fungible mode must have not zero in decimal_points parameter");
- }
+ let decimal_points = match mode {
+ CollectionMode::Fungible(points) => points,
+ _ => 0
+ };
// check params
ensure!(decimal_points < 100, "decimal_points parameter must be lower than 100");
@@ -189,20 +190,23 @@
let new_collection = CollectionType {
owner: who.clone(),
name: name,
- mode: collection_mode.clone(),
+ mode: mode.clone(),
access: AccessMode::Normal,
description: description,
decimal_points: decimal_points,
token_prefix: prefix,
next_item_id: next_id,
+ offchain_schema: Vec::new(),
custom_data_size: custom_data_size,
+ sponsor: T::AccountId::default(),
+ unconfirmed_sponsor: T::AccountId::default(),
};
// Add new collection to map
<Collection<T>>::insert(next_id, new_collection);
// call event
- Self::deposit_event(RawEvent::Created(next_id, mode, who.clone()));
+ Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));
Ok(())
}
@@ -285,19 +289,21 @@
<Balance<T>>::insert(collection_id, owner.clone(), new_balance);
// TODO: implement other modes
- ensure!(target_collection.mode == CollectionMode::NFT, "Collection type not implemented");
-
- if target_collection.mode == CollectionMode::NFT
+ match target_collection.mode
{
+ CollectionMode::NFT(_) => {
// Create nft item
- let item = NftItemType {
- collection: collection_id,
- owner: owner,
- data: properties,
- };
-
- Self::add_nft_item(item)?;
- }
+ let item = NftItemType {
+ collection: collection_id,
+ owner: owner,
+ data: properties,
+ };
+
+ Self::add_nft_item(item)?;
+
+ },
+ _ => ()
+ };
// call event
Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));
@@ -336,12 +342,11 @@
let target_collection = <Collection<T>>::get(collection_id);
// TODO: implement other modes
- ensure!(target_collection.mode == CollectionMode::NFT, "Collection type not implemented");
-
- if target_collection.mode == CollectionMode::NFT
+ match target_collection.mode
{
- Self::transfer_nft(collection_id, item_id, recipient)?;
- }
+ CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, recipient)?,
+ _ => ()
+ };
Ok(())
}
@@ -395,13 +400,12 @@
let target_collection = <Collection<T>>::get(collection_id);
- // TODO: implement other modes
- ensure!(target_collection.mode == CollectionMode::NFT, "Collection type not implemented");
-
- if target_collection.mode == CollectionMode::NFT
+ match target_collection.mode
{
- Self::transfer_nft(collection_id, item_id, recipient)?;
- }
+ CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, recipient)?,
+ // TODO: implement other modes
+ _ => ()
+ };
Ok(())
}
@@ -420,6 +424,22 @@
Ok(())
}
+
+ #[weight = 0]
+ pub fn set_offchain_schema(
+ origin,
+ collection_id: u64,
+ schema: Vec<u8>
+ ) -> DispatchResult {
+ let sender = ensure_signed(origin)?;
+ Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;
+
+ let mut target_collection = <Collection<T>>::get(collection_id);
+ target_collection.offchain_schema = schema;
+ <Collection<T>>::insert(collection_id, target_collection);
+
+ Ok(())
+ }
}
}
@@ -460,28 +480,14 @@
fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool{
- let mut result = false;
let target_collection = <Collection<T>>::get(collection_id);
- if target_collection.mode == CollectionMode::NFT
- {
- let item = <NftItemList<T>>::get(collection_id, item_id);
- result = item.owner == subject;
- }
-
- if target_collection.mode == CollectionMode::Fungible
- {
- let item = <FungibleItemList<T>>::get(collection_id, item_id);
- result = item.owner.contains(&subject);
- }
-
- if target_collection.mode == CollectionMode::ReFungible
- {
- let item = <ReFungibleItemList<T>>::get(collection_id, item_id);
- result = item.owner.iter().any(|i| i.owner == subject);
+ match target_collection.mode {
+ CollectionMode::NFT(_) => <NftItemList<T>>::get(collection_id, item_id).owner == subject,
+ CollectionMode::Fungible(_) => <FungibleItemList<T>>::get(collection_id, item_id).owner.contains(&subject),
+ CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id).owner.iter().any(|i| i.owner == subject),
+ CollectionMode::Invalid => false
}
-
- result
}
fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {
smart_contract/ink-types-node-runtime/README.mddiffbeforeafterboth--- a/smart_contract/ink-types-node-runtime/README.md
+++ b/smart_contract/ink-types-node-runtime/README.md
@@ -30,4 +30,67 @@
## Test
```
cargo +nightly test
+```
+
+## UI custom types
+```
+{
+ "Schedule": {
+ "version": "u32",
+ "put_code_per_byte_cost": "Gas",
+ "grow_mem_cost": "Gas",
+ "regular_op_cost": "Gas",
+ "return_data_per_byte_cost": "Gas",
+ "event_data_per_byte_cost": "Gas",
+ "event_per_topic_cost": "Gas",
+ "event_base_cost": "Gas",
+ "call_base_cost": "Gas",
+ "instantiate_base_cost": "Gas",
+ "dispatch_base_cost": "Gas",
+ "sandbox_data_read_cost": "Gas",
+ "sandbox_data_write_cost": "Gas",
+ "transfer_cost": "Gas",
+ "instantiate_cost": "Gas",
+ "max_event_topics": "u32",
+ "max_stack_height": "u32",
+ "max_memory_pages": "u32",
+ "max_table_size": "u32",
+ "enable_println": "bool",
+ "max_subject_len": "u32"
+ },
+ "CollectionMode": {
+ "_enum": {
+ "Invalid": null,
+ "NFT": "u32",
+ "Fungible": "u32",
+ "ReFungible": null
+ }
+ },
+ "NftItemType": {
+ "Collection": "u64",
+ "Owner": "AccountId",
+ "Data": "Vec<u8>"
+ },
+
+
+"CollectionType": {
+ "Owner": "AccountId",
+ "Mode": "u8",
+ "ModeParam": "u32",
+ "Access": "u8",
+ "NextItemId": "u64",
+ "DecimalPoints": "u32",
+ "Name": "Vec<u16>",
+ "Description": "Vec<u16>",
+ "TokenPrefix": "Vec<u8>",
+ "CustomDataSize": "u32",
+ "OffchainSchema": "Vec<u8>",
+ "Sponsor": "AccountId",
+ "UnconfirmedSponsor": "AccountId"
+ },
+ "RawData": "Vec<u8>",
+ "Address": "AccountId",
+ "LookupSource": "AccountId",
+ "Weight": "u64"
+}
```
\ No newline at end of file
smart_contract/ink-types-node-runtime/calls/lib.rsdiffbeforeafterboth1#![cfg_attr(not(feature = "std"), no_std)]23use ink_lang as ink;45#[ink::contract(version = "0.1.0", env = NodeRuntimeTypes)]6mod calls {7 use ink_core::env;8 use ink_prelude::vec::Vec;9 use ink_prelude::*;10 use ink_types_node_runtime::{calls as runtime_calls, NodeRuntimeTypes};11 use scale::{12 Decode,13 Encode,14 };1516 #[derive(Encode, Decode)]17 pub struct NftItemType {18 pub collection: u64,19 pub owner: AccountId,20 pub data: Vec<u8>,21 }2223 /// This simple dummy contract dispatches substrate runtime calls24 #[ink(storage)]25 struct Calls {}2627 impl Calls {28 #[ink(constructor)]29 fn new(&mut self) {}3031 #[ink(message)]32 fn transfer(&self, collection_id: u64, item_id: u64, new_owner: AccountId) {33 env::println(&format!(34 "transfer invoke_runtime params {:?}, {:?}, {:?} ",35 collection_id, item_id, new_owner36 ));3738 let transfer_call = runtime_calls::transfer(collection_id, item_id, new_owner);39 // dispatch the call to the runtime40 let result = self.env().invoke_runtime(&transfer_call);4142 // report result to console43 // NOTE: println should only be used on a development chain)44 env::println(&format!("transfer invoke_runtime result {:?}", result));45 }4647 // SafeTransfer4849 #[ink(message)]50 fn approve(&self, approved: AccountId, collection_id: u64, item_id: u64) {51 env::println(&format!(52 "approve invoke_runtime params {:?}, {:?}, {:?} ",53 approved, collection_id, item_id54 ));5556 let approve_call = runtime_calls::approve(approved, collection_id, item_id);57 // dispatch the call to the runtime58 let result = self.env().invoke_runtime(&approve_call);5960 // report result to console61 // NOTE: println should only be used on a development chain)62 env::println(&format!("approve invoke_runtime result {:?}", result));63 }6465 #[ink(message)]66 fn get_approved(&self, collection_id: u64, item_id: u64) -> Vec<AccountId> {67 let mut key = vec![68 // Precomputed: Twox128("Nft")69 244, 63, 251, 230, 30, 244, 104, 116, 157, 54, 23, 172, 26, 99, 196, 183,70 // Precomputed: Twox128("ApprovedList")71 86, 163, 236, 207, 221, 111, 252, 227, 254, 40, 142, 38, 40, 224, 192, 18,72 ];7374 let key_ext = vec![75 146, 214, 40, 117, 20, 27, 72, 25, 232, 204, 175, 194, 112, 244, 140, 100,76 ];77 key.extend_from_slice(&key_ext);7879 // collection id 80 let mut collection_bytes: Vec<u8> = collection_id.to_be_bytes().iter().cloned().collect();81 collection_bytes.reverse();82 key.extend_from_slice(&collection_bytes.as_slice());8384 // item id 85 let mut item_bytes: Vec<u8> = item_id.to_be_bytes().iter().cloned().collect();86 item_bytes.reverse();87 key.extend_from_slice(&item_bytes.as_slice()); 8889 // fetch from runtime storage90 let result = self.env().get_runtime_storage::<Vec<AccountId>>(&key[..]);91 92 match result {93 Some(Ok(accounts)) => { 94 env::println(&format!("get_approved result {:?}", accounts));95 accounts 96 },97 Some(Err(err)) => {98 env::println(&format!("Error reading {:?}", err));99 vec![]100 }101 None => {102 env::println(&format!("No data at key {:?}", key));103 vec![]104 }105 }106 }107108 #[ink(message)]109 fn transfer_from(&self, collection_id: u64, item_id: u64, new_owner: AccountId) {110 env::println(&format!(111 "transfer_from invoke_runtime params {:?}, {:?}, {:?} ",112 collection_id, item_id, new_owner113 ));114115 let transfer_from_call =116 runtime_calls::transfer_from(collection_id, item_id, new_owner);117 // dispatch the call to the runtime118 let result = self.env().invoke_runtime(&transfer_from_call);119120 // report result to console121 // NOTE: println should only be used on a development chain)122 env::println(&format!("transfer_from invoke_runtime result {:?}", result));123 }124125 // SafeTransferFrom126127 #[ink(message)]128 fn create_item(&self, collection_id: u64, properties: Vec<u8>) {129 env::println(&format!(130 "create_item invoke_runtime params {:?}, {:?} ",131 collection_id, properties132 ));133134 let create_item_call = runtime_calls::create_item(collection_id, properties);135 // dispatch the call to the runtime136 let result = self.env().invoke_runtime(&create_item_call);137138 // report result to console139 // NOTE: println should only be used on a development chain)140 env::println(&format!("create_item invoke_runtime result {:?}", result));141 }142143 #[ink(message)]144 fn burn_item(&self, collection_id: u64, item_id: u64) {145 env::println(&format!(146 "burn_item invoke_runtime params {:?}, {:?}",147 collection_id, item_id148 ));149150 let burn_item_call = runtime_calls::burn_item(collection_id, item_id);151 // dispatch the call to the runtime152 let result = self.env().invoke_runtime(&burn_item_call);153154 // report result to console155 // NOTE: println should only be used on a development chain)156 env::println(&format!("burn_item invoke_runtime result {:?}", result));157 }158159 // GetOwner160 #[ink(message)]161 fn get_owner(&self, collection_id: u64, token_id: u64) -> AccountId {162 let mut key = vec![163 // Precomputed: Twox128("Nft")164 244, 63, 251, 230, 30, 244, 104, 116, 157, 54, 23, 172, 26, 99, 196, 183,165 // Precomputed: Twox128("ItemList")166 116, 232, 175, 181, 237, 113, 149, 125, 139, 77, 55, 251, 115, 253, 29, 240,167 ];168169 let key_ext = vec![170 146, 214, 40, 117, 20, 27, 72, 25, 232, 204, 175, 194, 112, 244, 140, 100,171 ];172 key.extend_from_slice(&key_ext);173174 // collection id 175 let mut collection_bytes: Vec<u8> = collection_id.to_be_bytes().iter().cloned().collect();176 collection_bytes.reverse();177 key.extend_from_slice(&collection_bytes.as_slice());178179 // token id 180 let mut token_bytes: Vec<u8> = token_id.to_be_bytes().iter().cloned().collect();181 token_bytes.reverse();182 key.extend_from_slice(&token_bytes.as_slice()); 183184 // fetch from runtime storage 185 let result = self.env().get_runtime_storage::<NftItemType>(&key[..]);186 187 match result {188 Some(Ok(item)) => { 189 env::println(&format!("get_owner result {:?}", item.owner));190 item.owner191 },192 Some(Err(err)) => {193 env::println(&format!("Error reading {:?}", err));194 AccountId::from([0u8; 32])195 }196 None => {197 env::println(&format!("No data at key {:?}", key));198 AccountId::from([0u8; 32])199 }200 }201 }202203 #[ink(message)]204 fn get_balance_of(&self, collection_id: u64, owner: AccountId) -> u64 {205 let mut key = vec![206 // Precomputed: Twox128("Nft")207 244, 63, 251, 230, 30, 244, 104, 116, 157, 54, 23, 172, 26, 99, 196, 183,208 // Precomputed: Twox128("Balance")209 78, 168, 234, 12, 1, 250, 164, 43, 110, 179, 68, 168, 92, 71, 179, 135,210 ];211212 let key_ext = vec![213 15, 97, 136, 0, 76, 187, 168, 28, 239, 85, 170, 23, 77, 81, 248, 159,214 ];215 key.extend_from_slice(&key_ext);216217 // collection id 218 let mut collection_bytes: Vec<u8> = collection_id.to_be_bytes().iter().cloned().collect();219 collection_bytes.reverse();220 key.extend_from_slice(&collection_bytes.as_slice());221222 // owner223 key.extend_from_slice(&owner.encode()); 224225 // fetch from runtime storage226 let result = self.env().get_runtime_storage::<u64>(&key[..]);227 228 match result {229 Some(Ok(balance)) => { 230 env::println(&format!("get_balance_of result {:?}", balance));231 balance232 },233 Some(Err(err)) => {234 env::println(&format!("Error reading {:?}", err));235 0236 }237 None => {238 env::println(&format!("No data at key {:?}", key));239 0240 }241 }242 }243 }244}smart_contract/ink-types-node-runtime/src/calls.rsdiffbeforeafterboth--- a/smart_contract/ink-types-node-runtime/src/calls.rs
+++ b/smart_contract/ink-types-node-runtime/src/calls.rs
@@ -25,7 +25,7 @@
T::AccountId: Member + Codec,
{
#[allow(non_camel_case_types)]
- create_collection(Vec<u16>, Vec<u16>, Vec<u8>, u32),
+ create_collection(Vec<u16>, Vec<u16>, Vec<u8>, u8, u32, u32),
#[allow(non_camel_case_types)]
destroy_collection(u64),
@@ -40,19 +40,19 @@
remove_collection_admin(u64, T::AccountId),
#[allow(non_camel_case_types)]
- create_item(u64, Vec<u8>),
+ create_item(u64, Vec<u8>, T::AccountId),
#[allow(non_camel_case_types)]
burn_item(u64, u64),
#[allow(non_camel_case_types)]
- transfer(u64, u64, T::AccountId),
+ transfer(T::AccountId, u64, u64),
#[allow(non_camel_case_types)]
nft_approve(T::AccountId, u64, u64),
#[allow(non_camel_case_types)]
- nft_transfer_from(u64, u64, T::AccountId),
+ nft_transfer_from(T::AccountId, u64, u64),
#[allow(non_camel_case_types)]
nft_safe_transfer(u64, u64, T::AccountId),
@@ -66,13 +66,17 @@
collection_name: Vec<u16>,
collection_description: Vec<u16>,
token_prefix: Vec<u8>,
- custom_data_sz: u32,
+ mode: u8,
+ decimal_points: u32,
+ custom_data_size: u32,
) -> Call {
Nft::<NodeRuntimeTypes>::create_collection(
collection_name,
collection_description,
token_prefix,
- custom_data_sz,
+ mode,
+ decimal_points,
+ custom_data_size,
)
.into()
}
@@ -89,8 +93,8 @@
Nft::<NodeRuntimeTypes>::nft_transfer_from(collection_id, item_id, new_owner.into()).into()
}
-pub fn create_item(collection_id: u64, properties: Vec<u8>) -> Call {
- Nft::<NodeRuntimeTypes>::create_item(collection_id, properties).into()
+pub fn create_item(collection_id: u64, properties: Vec<u8>, owner: AccountId) -> Call {
+ Nft::<NodeRuntimeTypes>::create_item(collection_id, properties, owner).into()
}
pub fn burn_item(collection_id: u64, item_id: u64) -> Call {