--- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -130,7 +130,9 @@ ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char"); // Generate next collection ID - let next_id = NextCollectionID::get(); + let next_id = NextCollectionID::get() + .checked_add(1) + .expect("collection id error"); NextCollectionID::put(next_id); @@ -270,7 +272,9 @@ }; - let current_index = ::get(collection_id); + let current_index = ::get(collection_id) + .checked_add(1) + .expect("Item list index id error"); Self::add_token_index(collection_id, current_index, new_item.owner.clone())?; --- /dev/null +++ b/smart_contract/nft/.gitignore @@ -0,0 +1,9 @@ +# Ignore build artifacts from the local tests sub-crate. +/target/ + +# Ignore backup files creates by cargo fmt. +**/*.rs.bk + +# Remove Cargo.lock when creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock --- /dev/null +++ b/smart_contract/nft/.ink/abi_gen/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "abi-gen" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +publish = false + +[[bin]] +name = "abi-gen" +path = "main.rs" + +[dependencies] +contract = { path = "../..", package = "nft", default-features = false, features = ["ink-generate-abi"] } +ink_lang = { version = "2", git = "https://github.com/paritytech/ink", tag = "latest-v2", package = "ink_lang", default-features = false, features = ["ink-generate-abi"] } +serde = "1.0" +serde_json = "1.0" --- /dev/null +++ b/smart_contract/nft/.ink/abi_gen/main.rs @@ -0,0 +1,7 @@ +fn main() -> Result<(), std::io::Error> { + let abi = ::generate_abi(); + let contents = serde_json::to_string_pretty(&abi)?; + std::fs::create_dir("target").ok(); + std::fs::write("target/metadata.json", contents)?; + Ok(()) +} --- /dev/null +++ b/smart_contract/nft/Cargo.toml @@ -0,0 +1,73 @@ +[package] +name = "nft" +version = "0.1.0" +authors = ["[your_name] <[your_email]>"] +edition = "2018" + +[dependencies] + +ink_abi = { version = "2", git = "https://github.com/paritytech/ink", tag = "latest-v2", package = "ink_abi", default-features = false, features = ["derive"], optional = true } +ink_primitives = { version = "2", git = "https://github.com/paritytech/ink", tag = "latest-v2", package = "ink_primitives", default-features = false } +ink_core = { version = "2", git = "https://github.com/paritytech/ink", tag = "latest-v2", package = "ink_core", default-features = false } +ink_lang = { version = "2", git = "https://github.com/paritytech/ink", tag = "latest-v2", package = "ink_lang", default-features = false } +scale = { package = "parity-scale-codec", version = "1.2", default-features = false, features = ["derive"] } + +sp-core = { git = "https://github.com/paritytech/substrate/", package = "sp-core", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate/", package = "sp-runtime", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate/", package = "sp-io", default-features = false, features = ["disable_panic_handler", "disable_oom", "disable_allocator"] } +pallet-indices = { git = "https://github.com/paritytech/substrate/", package = "pallet-indices", default-features = false } + + +[dependencies.type-metadata] +git = "https://github.com/type-metadata/type-metadata.git" +rev = "02eae9f35c40c943b56af5b60616219f2b72b47d" +default-features = false +features = ["derive"] +optional = true + +[lib] +name = "nft" +path = "lib.rs" +crate-type = [ + # Used for normal contract Wasm blobs. + "cdylib", + # Required for ABI generation, and using this contract as a dependency. + # If using `cargo contract build`, it will be automatically disabled to produce a smaller Wasm binary + "rlib", +] + +[features] +default = ["test-env"] +std = [ + "ink_abi/std", + "ink_core/std", + "ink_primitives/std", + "scale/std", + "type-metadata/std", +] +test-env = [ + "std", + "ink_lang/test-env", +] +ink-generate-abi = [ + "std", + "ink_abi", + "type-metadata", + "ink_core/ink-generate-abi", + "ink_lang/ink-generate-abi", +] +ink-as-dependency = [] + +[profile.release] +panic = "abort" +lto = true +opt-level = "z" +overflow-checks = true + +[workspace] +members = [ + ".ink/abi_gen" +] +exclude = [ + ".ink" +] --- /dev/null +++ b/smart_contract/nft/lib.rs @@ -0,0 +1,215 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +use ink_core::env::EnvTypes; +use scale::{Codec, Decode, Encode}; +use sp_core::crypto::AccountId32; +use sp_runtime::traits::Member; +use pallet_indices::address::Address; +use core::{array::TryFromSliceError, convert::TryFrom}; +use ink_core::env::Clear; + +use ink_lang as ink; + +/// The default SRML hash type. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Encode, Decode)] +pub struct Hash([u8; 32]); + +impl From<[u8; 32]> for Hash { + fn from(hash: [u8; 32]) -> Hash { + Hash(hash) + } +} + +impl<'a> TryFrom<&'a [u8]> for Hash { + type Error = TryFromSliceError; + + fn try_from(bytes: &'a [u8]) -> Result { + let hash = <[u8; 32]>::try_from(bytes)?; + Ok(Hash(hash)) + } +} + +impl AsRef<[u8]> for Hash { + fn as_ref(&self) -> &[u8] { + &self.0[..] + } +} + +impl AsMut<[u8]> for Hash { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0[..] + } +} + +impl Clear for Hash { + fn is_clear(&self) -> bool { + self.as_ref().iter().all(|&byte| byte == 0x00) + } + + fn clear() -> Self { + Self([0x00; 32]) + } +} + +/// The default SRML moment type. +pub type Moment = u64; + +/// The default SRML blocknumber type. +pub type BlockNumber = u64; + +/// The default SRML AccountIndex type. +pub type AccountIndex = u32; + +/// The default timestamp type. +pub type Timestamp = u64; + +/// The default SRML balance type. +pub type Balance = u128; + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Encode, Decode)] +pub struct AccountId (AccountId32); + +impl From for AccountId { + fn from(account: AccountId32) -> Self { + AccountId(account) + } +} + +// #[cfg(feature = "ink-generate-abi")] +// impl HasTypeId for AccountId { +// fn type_id() -> TypeId { +// TypeIdArray::new(32, MetaType::new::()).into() +// } +// } + +// #[cfg(feature = "ink-generate-abi")] +// impl HasTypeDef for AccountId { +// fn type_def() -> TypeDef { +// TypeDef::builtin() +// } +// } + +/// Contract environment types defined in substrate node-runtime +#[cfg_attr(feature = "ink-generate-abi", derive(Metadata))] +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum NodeRuntimeTypes {} + +impl ink_core::env::EnvTypes for NodeRuntimeTypes { + type AccountId = AccountId; + type Balance = Balance; + type Hash = Hash; + type Timestamp = Timestamp; + type BlockNumber = BlockNumber; + type Call = Call; +} + +/// Default runtime Call type, a subset of the runtime Call module variants +/// +/// The codec indices of the modules *MUST* match those in the concrete runtime. +#[derive(Encode, Decode)] +#[cfg_attr(feature = "std", derive(Clone, PartialEq, Eq))] +pub enum Call { + #[codec(index = "6")] + Balances(Balances), +} + +/// Generic Balance Call, could be used with other runtimes +#[derive(Encode, Decode, Clone, PartialEq, Eq)] +pub enum Balances +where + T: EnvTypes, + T::AccountId: Member + Codec, + AccountIndex: Member + Codec, +{ + #[allow(non_camel_case_types)] + transfer(Address, #[codec(compact)] T::Balance), + #[allow(non_camel_case_types)] + set_balance( + Address, + #[codec(compact)] T::Balance, + #[codec(compact)] T::Balance, + ), +} + +#[ink::contract(version = "0.1.0")] +mod nft { + use ink_core::storage; + + /// Defines the storage of your contract. + /// Add new fields to the below struct in order + /// to add new static storage fields to your contract. + #[ink(storage)] + struct Nft { + /// Stores a single `bool` value on the storage. + value: storage::Value, + } + + impl Nft { + /// Constructor that initializes the `bool` value to the given `init_value`. + #[ink(constructor)] + fn new(&mut self, init_value: bool) { + self.value.set(init_value); + } + + /// Constructor that initializes the `bool` value to `false`. + /// + /// Constructors can delegate to other constructors. + #[ink(constructor)] + fn default(&mut self) { + self.new(false) + } + + /// A message that can be called on instantiated contracts. + /// This one flips the value of the stored `bool` from `true` + /// to `false` and vice versa. + #[ink(message)] + fn flip(&mut self) { + *self.value = !self.get(); + } + + /// Simply returns the current value of our `bool`. + #[ink(message)] + fn get(&self) -> bool { + *self.value + } + } +} + + +#[cfg(test)] +mod tests { + use crate::{calls, AccountIndex, NodeRuntimeTypes}; + use super::Call; + + use node_runtime::{self, Runtime}; + use pallet_indices::address; + use scale::{Decode, Encode}; + + #[test] + fn call_balance_transfer() { + let balance = 10_000; + let account_index = 0; + + let contract_address = calls::Address::Index(account_index); + let contract_transfer = + calls::Balances::::transfer(contract_address, balance); + let contract_call = Call::Balances(contract_transfer); + + let srml_address = address::Address::Index(account_index); + let srml_transfer = node_runtime::BalancesCall::::transfer(srml_address, balance); + let srml_call = node_runtime::Call::Balances(srml_transfer); + + let contract_call_encoded = contract_call.encode(); + let srml_call_encoded = srml_call.encode(); + + assert_eq!(srml_call_encoded, contract_call_encoded); + + let srml_call_decoded: node_runtime::Call = + Decode::decode(&mut contract_call_encoded.as_slice()) + .expect("Balances transfer call decodes to srml type"); + let srml_call_encoded = srml_call_decoded.encode(); + let contract_call_decoded: Call = Decode::decode(&mut srml_call_encoded.as_slice()) + .expect("Balances transfer call decodes back to contract type"); + assert!(contract_call == contract_call_decoded); + } +} \ No newline at end of file