--- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,14 @@ +FROM mcr.microsoft.com/vscode/devcontainers/rust:0-1 + +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive && \ + apt-get -y install --no-install-recommends libssl-dev pkg-config libclang-dev clang + +USER vscode + +RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash && \ + export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")" && \ + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && \ + nvm install v12.20.1 && \ + rustup toolchain install nightly-2020-10-01 && \ + rustup default nightly-2020-10-01 && \ + rustup target add wasm32-unknown-unknown \ No newline at end of file --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,18 @@ +{ + "name": "Rust", + "build": { + "dockerfile": "Dockerfile" + }, + "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ], + "settings": { + "terminal.integrated.shell.linux": "/bin/bash", + "lldb.executable": "/usr/bin/lldb", + "files.watcherExclude": { + "**/target/**": true + } + }, + "extensions": [ + "matklad.rust-analyzer" + ], + "remoteUser": "vscode" +} --- a/Cargo.lock +++ b/Cargo.lock @@ -543,9 +543,9 @@ [[package]] name = "bumpalo" -version = "3.5.0" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f07aa6688c702439a1be0307b6a94dffe1168569e45b9500c1372bc580740d59" +checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9" [[package]] name = "byte-slice-cast" @@ -985,9 +985,9 @@ [[package]] name = "ctor" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10bcb9d7dcbf7002aaffbb53eac22906b64cdcc127971dcc387d8eb7c95d5560" +checksum = "e8f45d9ad417bcef4817d614a501ab55cdd96a6fdb24f49aab89a54acfd66b19" dependencies = [ "quote", "syn", @@ -1032,9 +1032,9 @@ [[package]] name = "data-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993a608597367c6377b258c25d7120740f00ed23a2252b729b1932dd7866f908" +checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" [[package]] name = "derive_more" @@ -1151,7 +1151,7 @@ "ed25519", "rand 0.7.3", "serde", - "sha2 0.9.2", + "sha2 0.9.3", "zeroize", ] @@ -1342,9 +1342,9 @@ [[package]] name = "flate2" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7411863d55df97a419aa64cb4d2f167103ea9d767e2c54a1868b7ac3f6b47129" +checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0" dependencies = [ "cfg-if 1.0.0", "crc32fast", @@ -1907,7 +1907,7 @@ "http 0.2.3", "indexmap", "slab", - "tokio 0.2.24", + "tokio 0.2.25", "tokio-util", "tracing", "tracing-futures", @@ -2064,9 +2064,9 @@ [[package]] name = "httparse" -version = "1.3.4" +version = "1.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" +checksum = "615caabe2c3160b313d52ccc905335f4ed5f10881dd63dc5699d47e90be85691" [[package]] name = "httpdate" @@ -2129,9 +2129,9 @@ "httparse", "httpdate", "itoa", - "pin-project 1.0.4", + "pin-project 1.0.5", "socket2", - "tokio 0.2.24", + "tokio 0.2.25", "tower-service", "tracing", "want 0.3.0", @@ -2150,7 +2150,7 @@ "log", "rustls 0.18.1", "rustls-native-certs", - "tokio 0.2.24", + "tokio 0.2.25", "tokio-rustls", "webpki", ] @@ -2553,9 +2553,9 @@ [[package]] name = "libc" -version = "0.2.82" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929" +checksum = "7ccac4b00700875e6a07c6cde370d44d32fa01c5a65cdd2fca6858c479d28bb3" [[package]] name = "libloading" @@ -2606,7 +2606,7 @@ "libp2p-yamux", "parity-multiaddr", "parking_lot 0.11.1", - "pin-project 1.0.4", + "pin-project 1.0.5", "smallvec 1.6.1", "wasm-timer", ] @@ -2631,13 +2631,13 @@ "multistream-select", "parity-multiaddr", "parking_lot 0.11.1", - "pin-project 1.0.4", + "pin-project 1.0.5", "prost", "prost-build", "rand 0.7.3", "ring", "rw-stream-sink", - "sha2 0.9.2", + "sha2 0.9.3", "smallvec 1.6.1", "thiserror", "unsigned-varint 0.6.0", @@ -2715,7 +2715,7 @@ "prost-build", "rand 0.7.3", "regex", - "sha2 0.9.2", + "sha2 0.9.3", "smallvec 1.6.1", "unsigned-varint 0.6.0", "wasm-timer", @@ -2755,7 +2755,7 @@ "prost", "prost-build", "rand 0.7.3", - "sha2 0.9.2", + "sha2 0.9.3", "smallvec 1.6.1", "uint", "unsigned-varint 0.6.0", @@ -2817,7 +2817,7 @@ "prost", "prost-build", "rand 0.7.3", - "sha2 0.9.2", + "sha2 0.9.3", "snow", "static_assertions", "x25519-dalek", @@ -2864,7 +2864,7 @@ dependencies = [ "futures 0.3.12", "log", - "pin-project 1.0.4", + "pin-project 1.0.5", "rand 0.7.3", "salsa20", "sha3", @@ -2892,9 +2892,9 @@ [[package]] name = "libp2p-swarm" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4f89ebb4d8953bda12623e9871959fe728dea3bf6eae0421dc9c42dc821e488" +checksum = "7955b973e1fd2bd61ffd43ce261c1223f61f4aacd5bae362a924993f9a25fd98" dependencies = [ "either", "futures 0.3.12", @@ -3074,9 +3074,9 @@ [[package]] name = "lru" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3aae342b73d57ad0b8b364bd12584819f2c1fe9114285dfcf8b0722607671635" +checksum = "fe2382d8ed3918ea4ba70d98d5b74e47a168e0331965f3f17cdbc748bdebc5a3" dependencies = [ "hashbrown", ] @@ -3310,7 +3310,7 @@ "digest 0.9.0", "generic-array 0.14.4", "multihash-derive", - "sha2 0.9.2", + "sha2 0.9.3", "unsigned-varint 0.5.1", ] @@ -3343,7 +3343,7 @@ "bytes 1.0.1", "futures 0.3.12", "log", - "pin-project 1.0.4", + "pin-project 1.0.5", "smallvec 1.6.1", "unsigned-varint 0.6.0", ] @@ -4269,11 +4269,11 @@ [[package]] name = "pin-project" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b70b68509f17aa2857863b6fa00bf21fc93674c7a8893de2f469f6aa7ca2f2" +checksum = "96fa8ebb90271c4477f144354485b8068bd8f6b78b428b01ba892ca26caf0b63" dependencies = [ - "pin-project-internal 1.0.4", + "pin-project-internal 1.0.5", ] [[package]] @@ -4289,9 +4289,9 @@ [[package]] name = "pin-project-internal" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caa25a6393f22ce819b0f50e0be89287292fda8d425be38ee0ca14c4931d9e71" +checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b" dependencies = [ "proc-macro2", "quote", @@ -4862,9 +4862,9 @@ [[package]] name = "ring" -version = "0.16.19" +version = "0.16.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "024a1e66fea74c66c66624ee5622a7ff0e4b73a13b4f5c326ddb50c708944226" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" dependencies = [ "cc", "libc", @@ -5114,7 +5114,7 @@ "structopt", "thiserror", "tiny-bip39", - "tokio 0.2.24", + "tokio 0.2.25", ] [[package]] @@ -6037,9 +6037,9 @@ [[package]] name = "sha-1" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce3cdf1b5e620a498ee6f2a171885ac7e22f0e12089ec4b3d22b84921792507c" +checksum = "f4b312c3731e3fe78a185e6b9b911a7aa715b8e31cce117975219aab2acf285d" dependencies = [ "block-buffer 0.9.0", "cfg-if 1.0.0", @@ -6062,9 +6062,9 @@ [[package]] name = "sha2" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e7aab86fe2149bad8c507606bdb3f4ef5e7b2380eb92350f56122cca72a42a8" +checksum = "fa827a14b29ab7f44778d14a88d3cb76e949c45083f7dbfa507d0cb699dc12de" dependencies = [ "block-buffer 0.9.0", "cfg-if 1.0.0", @@ -6171,7 +6171,7 @@ "rand_core 0.5.1", "ring", "rustc_version", - "sha2 0.9.2", + "sha2 0.9.3", "subtle 2.4.0", "x25519-dalek", ] @@ -6200,7 +6200,7 @@ "httparse", "log", "rand 0.7.3", - "sha-1 0.9.2", + "sha-1 0.9.3", ] [[package]] @@ -6428,7 +6428,7 @@ "schnorrkel", "secrecy", "serde", - "sha2 0.9.2", + "sha2 0.9.3", "sp-debug-derive", "sp-externalities", "sp-runtime-interface", @@ -6973,7 +6973,7 @@ "hyper 0.13.9", "log", "prometheus", - "tokio 0.2.24", + "tokio 0.2.25", ] [[package]] @@ -7083,9 +7083,9 @@ [[package]] name = "thread_local" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8208a331e1cb318dd5bd76951d2b8fc48ca38a69f5f4e4af1b6a9f8c6236915" +checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" dependencies = [ "once_cell", ] @@ -7122,7 +7122,7 @@ "pbkdf2 0.4.0", "rand 0.7.3", "rustc-hash", - "sha2 0.9.2", + "sha2 0.9.3", "thiserror", "unicode-normalization", "zeroize", @@ -7178,9 +7178,9 @@ [[package]] name = "tokio" -version = "0.2.24" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099837d3464c16a808060bb3f02263b412f6fafcb5d01c533d309985fbeebe48" +checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092" dependencies = [ "bytes 0.5.6", "fnv", @@ -7302,7 +7302,7 @@ dependencies = [ "futures-core", "rustls 0.18.1", - "tokio 0.2.24", + "tokio 0.2.25", "webpki", ] @@ -7412,7 +7412,7 @@ "futures-sink", "log", "pin-project-lite 0.1.11", - "tokio 0.2.24", + "tokio 0.2.25", ] [[package]] @@ -7432,9 +7432,9 @@ [[package]] name = "tracing" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3" +checksum = "f7d40a22fd029e33300d8d89a5cc8ffce18bb7c587662f54629e94c9de5487f3" dependencies = [ "cfg-if 1.0.0", "log", @@ -7445,9 +7445,9 @@ [[package]] name = "tracing-attributes" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e0ccfc3378da0cce270c946b676a376943f5cd16aeba64568e7939806f4ada" +checksum = "43f080ea7e4107844ef4766459426fa2d5c1ada2e47edba05dc7fa99d9629f47" dependencies = [ "proc-macro2", "quote", @@ -7518,9 +7518,9 @@ [[package]] name = "trie-db" -version = "0.22.2" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc176c377eb24d652c9c69c832c832019011b6106182bf84276c66b66d5c9a6" +checksum = "ec051edf7f0fc9499a2cb0947652cab2148b9d7f61cee7605e312e9f970dacaf" dependencies = [ "hash-db", "hashbrown", @@ -8032,18 +8032,18 @@ [[package]] name = "wast" -version = "32.0.0" +version = "33.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c24a3ee360d01d60ed0a0f960ab76a6acce64348cdb0bf8699c2a866fad57c7c" +checksum = "1d04fe175c7f78214971293e7d8875673804e736092206a3a4544dbc12811c1b" dependencies = [ "leb128", ] [[package]] name = "wat" -version = "1.0.33" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e8f7f34773fa6318e8897283abf7941c1f250faae4e1a52f82df09c3bad7cce" +checksum = "7ec9c6ee01ae07a26adadcdfed22c7a97e0b8cbee9c06e0e96076ece5aeb5cfe" dependencies = [ "wast", ] --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,9 @@ # ===== BUILD ====== -FROM phusion/baseimage:0.10.2 as builder +FROM phusion/baseimage:18.04-1.0.0 as builder LABEL maintainer="gz@usetech.com" -ENV WASM_TOOLCHAIN=nightly-2020-10-01 +ENV WASM_TOOLCHAIN=nightly-2021-01-27 ARG PROFILE=release @@ -37,7 +37,7 @@ # ===== RUN ====== -FROM phusion/baseimage:0.10.2 +FROM phusion/baseimage:18.04-1.0.0 ARG PROFILE=release COPY --from=builder /nft_parachain/target/$PROFILE/nft /usr/local/bin --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ ```bash rustup toolchain install 1.49.0 -rustup toolchain install nightly-2020-10-01 +rustup toolchain install nightly-2020-01-27 rustup default nightly-2021-01-27 ``` --- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -54,6 +54,7 @@ mod default_weights; pub const MAX_DECIMAL_POINTS: DecimalPoints = 30; +pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000; pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000; pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000; @@ -71,8 +72,7 @@ NFT, // decimal points Fungible(DecimalPoints), - // decimal points - ReFungible(DecimalPoints), + ReFungible, } impl Default for CollectionMode { @@ -87,7 +87,7 @@ CollectionMode::Invalid => 0, CollectionMode::NFT => 1, CollectionMode::Fungible(_) => 2, - CollectionMode::ReFungible(_) => 3, + CollectionMode::ReFungible => 3, } } } @@ -266,6 +266,7 @@ pub struct CreateReFungibleData { pub const_data: Vec, pub variable_data: Vec, + pub pieces: u128, } #[derive(Encode, Decode, Debug, Clone, PartialEq)] @@ -377,7 +378,9 @@ /// Collection limit bounds per collection exceeded CollectionLimitBoundsExceeded, /// Schema data size limit bound exceeded - SchemaDataLimitExceeded + SchemaDataLimitExceeded, + /// Maximum refungibility exceeded + WrongRefungiblePieces } } @@ -548,7 +551,6 @@ let decimal_points = match mode { CollectionMode::Fungible(points) => points, - CollectionMode::ReFungible(points) => points, _ => 0 }; @@ -933,7 +935,7 @@ Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?; Self::validate_create_item_args(&target_collection, &data)?; - Self::create_item_no_validation(collection_id, &target_collection, owner, data)?; + Self::create_item_no_validation(collection_id, owner, data)?; Ok(()) } @@ -973,7 +975,7 @@ Self::validate_create_item_args(&target_collection, data)?; } for data in &items_data { - Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?; + Self::create_item_no_validation(collection_id, owner.clone(), data.clone())?; } Ok(()) @@ -1012,7 +1014,7 @@ { CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?, CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, collection_id, value)?, - CollectionMode::ReFungible(_) => Self::burn_refungible_item(collection_id, item_id, &sender)?, + CollectionMode::ReFungible => Self::burn_refungible_item(collection_id, item_id, &sender)?, _ => () }; @@ -1122,8 +1124,8 @@ // Check approval let mut approval: u128 = 0; - if >::contains_key(collection_id, (item_id, &from, &recipient)) { - approval = >::get(collection_id, (item_id, &from, &recipient)); + if >::contains_key(collection_id, (item_id, &from, &sender)) { + approval = >::get(collection_id, (item_id, &from, &sender)); ensure!(approval >= value, Error::::TokenValueNotEnough); appoved_transfer = true; } @@ -1144,17 +1146,17 @@ // Reduce approval by transferred amount or remove if remaining approval drops to 0 if approval.checked_sub(value).unwrap_or(0) > 0 { - >::insert(collection_id, (item_id, &from, &recipient), approval - value); + >::insert(collection_id, (item_id, &from, &sender), approval - value); } else { - >::remove(collection_id, (item_id, &from, &recipient)); + >::remove(collection_id, (item_id, &from, &sender)); } match target_collection.mode { CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?, CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, value, &from, &recipient)?, - CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?, + CollectionMode::ReFungible => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?, _ => () }; @@ -1211,7 +1213,7 @@ match target_collection.mode { CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?, - CollectionMode::ReFungible(_) => Self::set_re_fungible_variable_data(collection_id, item_id, data)?, + CollectionMode::ReFungible => Self::set_re_fungible_variable_data(collection_id, item_id, data)?, CollectionMode::Fungible(_) => fail!(Error::::CantStoreMetadataInFungibleTokens), _ => fail!(Error::::UnexpectedCollectionType) }; @@ -1551,7 +1553,7 @@ { CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?, CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, value, &sender, &recipient)?, - CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?, + CollectionMode::ReFungible => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?, _ => () }; @@ -1603,12 +1605,16 @@ fail!(Error::::NotFungibleDataUsedToMintFungibleCollectionToken); } }, - CollectionMode::ReFungible(_) => { + CollectionMode::ReFungible => { if let CreateItemData::ReFungible(data) = data { // check sizes ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::::TokenConstDataLimitExceeded); ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::::TokenVariableDataLimitExceeded); + + // Check refungibility limits + ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::::WrongRefungiblePieces); + ensure!(data.pieces > 0, Error::::WrongRefungiblePieces); } else { fail!(Error::::NotReFungibleDataUsedToMintReFungibleCollectionToken); } @@ -1619,7 +1625,7 @@ Ok(()) } - fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType, owner: T::AccountId, data: CreateItemData) -> DispatchResult { + fn create_item_no_validation(collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult { match data { CreateItemData::NFT(data) => { @@ -1636,8 +1642,7 @@ }, CreateItemData::ReFungible(data) => { let mut owner_list = Vec::new(); - let value = (10 as u128).pow(collection.decimal_points as u32); - owner_list.push(Ownership {owner: owner.clone(), fraction: value}); + owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces}); let item = ReFungibleItemType { owner: owner_list, @@ -1866,7 +1871,7 @@ CollectionMode::Fungible(_) => { >::contains_key(collection_id, &subject) } - CollectionMode::ReFungible(_) => { + CollectionMode::ReFungible => { >::get(collection_id, item_id) .owner .iter() @@ -1895,7 +1900,7 @@ { CollectionMode::NFT => >::contains_key(collection_id, item_id), CollectionMode::Fungible(_) => >::contains_key(collection_id, owner), - CollectionMode::ReFungible(_) => >::contains_key(collection_id, item_id), + CollectionMode::ReFungible => >::contains_key(collection_id, item_id), _ => false }; @@ -2421,7 +2426,7 @@ sponsored } - CollectionMode::ReFungible(_) => { + CollectionMode::ReFungible => { // get correct limit let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 { --- a/pallets/nft/src/mock.rs +++ b/pallets/nft/src/mock.rs @@ -1,26 +1,21 @@ // Creating mock runtime here -use crate::{Module, Trait}; - -use pallet_contracts::{ - ContractAddressFor, TrieId, TrieIdGenerator, -}; +use crate::{Module, Config}; use frame_support::{ impl_outer_origin, parameter_types, - weights::{ - // constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}, - Weight, IdentityFee, - }, + weights::{Weight, IdentityFee}, }; use frame_system as system; -use transaction_payment; +use system::limits::{BlockLength, BlockWeights}; +use transaction_payment::{self, CurrencyAdapter}; use sp_core::H256; use sp_runtime::{ testing::Header, - traits::{BlakeTwo256, IdentityLookup, Saturating}, + traits::{BlakeTwo256, IdentityLookup}, Perbill, }; +use pallet_contracts::WeightInfo; pub use pallet_balances; impl_outer_origin! { @@ -34,14 +29,12 @@ pub struct Test; parameter_types! { pub const BlockHashCount: u64 = 250; - pub const MaximumBlockWeight: Weight = 1024; - pub const MaximumBlockLength: u32 = 2 * 1024; - pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75); - pub MaximumExtrinsicWeight: Weight = AvailableBlockRatio::get() - .saturating_sub(Perbill::from_percent(10)) * MaximumBlockWeight::get(); + pub TestBlockWeights: BlockWeights = BlockWeights::default(); + pub TestBlockLength: BlockLength = BlockLength::default(); + pub SS58Prefix: u8 = 0; } -impl system::Trait for Test { +impl system::Config for Test { type BaseCallFilter = (); type Origin = Origin; type Call = (); @@ -54,18 +47,15 @@ type Header = Header; type Event = (); type BlockHashCount = BlockHashCount; - type MaximumBlockWeight = MaximumBlockWeight; + type BlockWeights = TestBlockWeights; type DbWeight = (); - type BlockExecutionWeight = (); - type ExtrinsicBaseWeight = (); - type MaximumExtrinsicWeight = MaximumBlockWeight; - type MaximumBlockLength = MaximumBlockLength; - type AvailableBlockRatio = AvailableBlockRatio; + type BlockLength = TestBlockLength; type Version = (); type PalletInfo = (); type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); + type SS58Prefix = SS58Prefix; type SystemWeightInfo = (); } @@ -75,7 +65,7 @@ } type System = frame_system::Module; -impl pallet_balances::Trait for Test { +impl pallet_balances::Config for Test { type AccountStore = System; type Balance = u64; type DustRemoval = (); @@ -88,9 +78,9 @@ parameter_types! { pub const TransactionByteFee: u64 = 1; } -impl transaction_payment::Trait for Test { - type Currency = pallet_balances::Module; - type OnTransactionPayment = (); + +impl transaction_payment::Config for Test { + type OnChargeTransaction = CurrencyAdapter, ()>; type TransactionByteFee = TransactionByteFee; type WeightToFee = IdentityFee; type FeeMultiplierUpdate = (); @@ -100,7 +90,7 @@ parameter_types! { pub const MinimumPeriod: u64 = 1; } -impl pallet_timestamp::Trait for Test { +impl pallet_timestamp::Config for Test { type Moment = u64; type OnTimestampSet = (); type MinimumPeriod = MinimumPeriod; @@ -112,49 +102,45 @@ parameter_types! { pub const TombstoneDeposit: u64 = 1; - pub const RentByteFee: u64 = 1; - pub const RentDepositOffset: u64 = 1; + pub const DepositPerContract: u64 = 1; + pub const DepositPerStorageByte: u64 = 1; + pub const DepositPerStorageItem: u64 = 1; + pub RentFraction: Perbill = Perbill::from_rational_approximation(1u32, 30 * 24 * 60 * 10); pub const SurchargeReward: u64 = 1; -} - -pub struct DummyTrieIdGenerator; -impl TrieIdGenerator for DummyTrieIdGenerator { - fn trie_id(account_id: &u64) -> TrieId { - let new_seed = *account_id + 1; - let mut res = vec![]; - res.extend_from_slice(&new_seed.to_le_bytes()); - res.extend_from_slice(&account_id.to_le_bytes()); - res - } -} - -pub struct DummyContractAddressFor; -impl ContractAddressFor for DummyContractAddressFor { - fn contract_address_for(_code_hash: &H256, _data: &[u8], origin: &u64) -> u64 { - *origin + 1 - } + pub const SignedClaimHandicap: u32 = 2; + pub const MaxDepth: u32 = 32; + pub const MaxValueSize: u32 = 16 * 1024; + pub DeletionWeightLimit: Weight = Perbill::from_percent(10) * + TestBlockWeights::get().max_block; + pub DeletionQueueDepth: u32 = ((DeletionWeightLimit::get() / ( + ::WeightInfo::on_initialize_per_queue_item(1) - + ::WeightInfo::on_initialize_per_queue_item(0) + )) / 5) as u32; } -impl pallet_contracts::Trait for Test { +impl pallet_contracts::Config for Test { type Time = Timestamp; type Randomness = Randomness; type Currency = pallet_balances::Module; type Event = (); - type DetermineContractAddress = DummyContractAddressFor; - type TrieIdGenerator = DummyTrieIdGenerator; type RentPayment = (); - type SignedClaimHandicap = pallet_contracts::DefaultSignedClaimHandicap; + type SignedClaimHandicap = SignedClaimHandicap; type TombstoneDeposit = TombstoneDeposit; - type StorageSizeOffset = pallet_contracts::DefaultStorageSizeOffset; - type RentByteFee = RentByteFee; - type RentDepositOffset = RentDepositOffset; + type DepositPerContract = DepositPerContract; + type DepositPerStorageByte = DepositPerStorageByte; + type DepositPerStorageItem = DepositPerStorageItem; + type RentFraction = RentFraction; type SurchargeReward = SurchargeReward; - type MaxDepth = pallet_contracts::DefaultMaxDepth; - type MaxValueSize = pallet_contracts::DefaultMaxValueSize; + type DeletionWeightLimit = DeletionWeightLimit; + type MaxDepth = MaxDepth; + type DeletionQueueDepth = DeletionQueueDepth; + type MaxValueSize = MaxValueSize; + type ChainExtension = (); type WeightPrice = (); + type WeightInfo = pallet_contracts::weights::SubstrateWeight; } -impl Trait for Test { +impl Config for Test { type Event = (); type WeightInfo = (); --- a/pallets/nft/src/tests.rs +++ b/pallets/nft/src/tests.rs @@ -35,7 +35,7 @@ } fn default_re_fungible_data () -> CreateReFungibleData { - CreateReFungibleData { const_data: vec![1, 2, 3], variable_data: vec![3, 2, 1] } + CreateReFungibleData { const_data: vec![1, 2, 3], variable_data: vec![3, 2, 1], pieces: 1023 } } fn create_test_collection_for_owner(mode: &CollectionMode, owner: u64, id: CollectionId) -> CollectionId { @@ -114,26 +114,6 @@ } #[test] -fn create_re_fungible_collection_fails_with_large_decimal_numbers() { - new_test_ext().execute_with(|| { - default_limits(); - - let col_name1: Vec = "Test1\0".encode_utf16().collect::>(); - let col_desc1: Vec = "TestDescription1\0".encode_utf16().collect::>(); - let token_prefix1: Vec = b"token_prefix1\0".to_vec(); - - let origin1 = Origin::signed(1); - assert_noop!(TemplateModule::create_collection( - origin1, - col_name1, - col_desc1, - token_prefix1, - CollectionMode::ReFungible(MAX_DECIMAL_POINTS + 1) - ), Error::::CollectionDecimalPointLimitExceeded); - }); -} - -#[test] fn create_nft_item() { new_test_ext().execute_with(|| { default_limits(); @@ -176,7 +156,7 @@ fn create_refungible_item() { new_test_ext().execute_with(|| { default_limits(); - let collection_id = create_test_collection(&CollectionMode::ReFungible(3), 1); + let collection_id = create_test_collection(&CollectionMode::ReFungible, 1); let data = default_re_fungible_data(); create_test_item(collection_id, &data.clone().into()); @@ -192,7 +172,7 @@ TemplateModule::refungible_item_id(collection_id, 1).owner[0], Ownership { owner: 1, - fraction: 1000 + fraction: 1023 } ); }); @@ -203,7 +183,7 @@ new_test_ext().execute_with(|| { default_limits(); - create_test_collection(&CollectionMode::ReFungible(3), 1); + create_test_collection(&CollectionMode::ReFungible, 1); let origin1 = Origin::signed(1); @@ -224,7 +204,7 @@ item.owner[0], Ownership { owner: 1, - fraction: 1000 + fraction: 1023 } ); } @@ -312,7 +292,7 @@ new_test_ext().execute_with(|| { default_limits(); - let collection_id = create_test_collection(&CollectionMode::ReFungible(3), 1); + let collection_id = create_test_collection(&CollectionMode::ReFungible, 1); let data = default_re_fungible_data(); create_test_item(collection_id, &data.clone().into()); @@ -331,23 +311,23 @@ TemplateModule::refungible_item_id(collection_id, 1).owner[0], Ownership { owner: 1, - fraction: 1000 + fraction: 1023 } ); - assert_eq!(TemplateModule::balance_count(1, 1), 1000); + assert_eq!(TemplateModule::balance_count(1, 1), 1023); assert_eq!(TemplateModule::address_tokens(1, 1), [1]); // change owner scenario - assert_ok!(TemplateModule::transfer(origin1.clone(), 2, 1, 1, 1000)); + assert_ok!(TemplateModule::transfer(origin1.clone(), 2, 1, 1, 1023)); assert_eq!( TemplateModule::refungible_item_id(1, 1).owner[0], Ownership { owner: 2, - fraction: 1000 + fraction: 1023 } ); assert_eq!(TemplateModule::balance_count(1, 1), 0); - assert_eq!(TemplateModule::balance_count(1, 2), 1000); + assert_eq!(TemplateModule::balance_count(1, 2), 1023); // assert_eq!(TemplateModule::address_tokens(1, 1), []); assert_eq!(TemplateModule::address_tokens(1, 2), [1]); @@ -357,7 +337,7 @@ TemplateModule::refungible_item_id(1, 1).owner[0], Ownership { owner: 2, - fraction: 500 + fraction: 523 } ); assert_eq!( @@ -367,7 +347,7 @@ fraction: 500 } ); - assert_eq!(TemplateModule::balance_count(1, 2), 500); + assert_eq!(TemplateModule::balance_count(1, 2), 523); assert_eq!(TemplateModule::balance_count(1, 3), 500); assert_eq!(TemplateModule::address_tokens(1, 2), [1]); assert_eq!(TemplateModule::address_tokens(1, 3), [1]); @@ -378,7 +358,7 @@ TemplateModule::refungible_item_id(1, 1).owner[0], Ownership { owner: 2, - fraction: 300 + fraction: 323 } ); assert_eq!( @@ -388,7 +368,7 @@ fraction: 700 } ); - assert_eq!(TemplateModule::balance_count(1, 2), 300); + assert_eq!(TemplateModule::balance_count(1, 2), 323); assert_eq!(TemplateModule::balance_count(1, 3), 700); assert_eq!(TemplateModule::address_tokens(1, 2), [1]); assert_eq!(TemplateModule::address_tokens(1, 3), [1]); @@ -454,7 +434,7 @@ assert_ok!(TemplateModule::transfer_from( origin2.clone(), 1, - 2, + 3, 1, 1, 1 @@ -508,7 +488,7 @@ 1, 1 )); - assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 4); + assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 4); }); } @@ -517,7 +497,7 @@ new_test_ext().execute_with(|| { default_limits(); - let collection_id = create_test_collection(&CollectionMode::ReFungible(3), 1); + let collection_id = create_test_collection(&CollectionMode::ReFungible, 1); let origin1 = Origin::signed(1); let origin2 = Origin::signed(2); @@ -525,7 +505,7 @@ let data = default_re_fungible_data(); create_test_item(collection_id, &data.into()); - assert_eq!(TemplateModule::balance_count(1, 1), 1000); + assert_eq!(TemplateModule::balance_count(1, 1), 1023); assert_eq!(TemplateModule::address_tokens(1, 1), [1]); assert_ok!(TemplateModule::set_mint_permission( @@ -543,10 +523,8 @@ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3)); // do approve - assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5)); - assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5); - assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1, 1000)); - assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 1000); + assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 1023)); + assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 1023); assert_ok!(TemplateModule::transfer_from( origin2.clone(), @@ -556,14 +534,14 @@ 1, 100 )); - assert_eq!(TemplateModule::balance_count(1, 1), 900); + assert_eq!(TemplateModule::balance_count(1, 1), 923); assert_eq!(TemplateModule::balance_count(1, 3), 100); assert_eq!(TemplateModule::address_tokens(1, 1), [1]); assert_eq!(TemplateModule::address_tokens(1, 3), [1]); assert_eq!( - TemplateModule::approved(1, (1, 1, 3)), - 900 + TemplateModule::approved(1, (1, 1, 2)), + 923 ); }); } @@ -618,10 +596,8 @@ assert_eq!(TemplateModule::balance_count(1, 1), 1); assert_eq!(TemplateModule::balance_count(1, 3), 4); - assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5); - assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 1); + assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 1); - assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5)); assert_noop!(TemplateModule::transfer_from( origin2.clone(), 1, @@ -721,7 +697,7 @@ new_test_ext().execute_with(|| { default_limits(); - let collection_id = create_test_collection(&CollectionMode::ReFungible(3), 1); + let collection_id = create_test_collection(&CollectionMode::ReFungible, 1); let origin1 = Origin::signed(1); assert_ok!(TemplateModule::set_mint_permission( @@ -742,12 +718,12 @@ create_test_item(collection_id, &data.into()); // check balance (collection with id = 1, user id = 2) - assert_eq!(TemplateModule::balance_count(1, 1), 1000); + assert_eq!(TemplateModule::balance_count(1, 1), 1023); // burn item - assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1, 1000)); + assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1, 1023)); assert_noop!( - TemplateModule::burn_item(origin1.clone(), 1, 1, 1000), + TemplateModule::burn_item(origin1.clone(), 1, 1, 1023), Error::::TokenNotFound ); @@ -811,7 +787,7 @@ let nft_collection_id = create_test_collection(&CollectionMode::NFT, 1); let fungible_collection_id = create_test_collection(&CollectionMode::Fungible(3), 2); - let re_fungible_collection_id = create_test_collection(&CollectionMode::ReFungible(3), 3); + let re_fungible_collection_id = create_test_collection(&CollectionMode::ReFungible, 3); // check balance before assert_eq!(TemplateModule::balance_count(nft_collection_id, 1), 0); @@ -830,7 +806,7 @@ // check balance (collection with id = 1, user id = 1) assert_eq!(TemplateModule::balance_count(nft_collection_id, 1), 1); assert_eq!(TemplateModule::balance_count(fungible_collection_id, 1), 5); - assert_eq!(TemplateModule::balance_count(re_fungible_collection_id, 1), 1000); + assert_eq!(TemplateModule::balance_count(re_fungible_collection_id, 1), 1023); assert_eq!(TemplateModule::nft_item_id(nft_collection_id, 1).owner, 1); assert_eq!(TemplateModule::fungible_item_id(fungible_collection_id, 1).value, 5); assert_eq!(TemplateModule::refungible_item_id(re_fungible_collection_id, 1).owner[0].owner, 1); @@ -1909,7 +1885,7 @@ new_test_ext().execute_with(|| { default_limits(); - let collection_id = create_test_collection(&CollectionMode::ReFungible(3), 1); + let collection_id = create_test_collection(&CollectionMode::ReFungible, 1); let origin1 = Origin::signed(1); @@ -1986,7 +1962,7 @@ })); - let collection_id = create_test_collection(&CollectionMode::ReFungible(3), 1); + let collection_id = create_test_collection(&CollectionMode::ReFungible, 1); let origin1 = Origin::signed(1); --- a/runtime_types.json +++ b/runtime_types.json @@ -1,27 +1,4 @@ { - "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" - }, "AccessMode": { "_enum": [ "Normal", @@ -34,7 +11,7 @@ "Invalid": null, "NFT": null, "Fungible": "DecimalPoints", - "ReFungible": "DecimalPoints" + "ReFungible": null } }, "Ownership": { @@ -84,7 +61,8 @@ }, "CreateReFungibleData": { "const_data": "Vec", - "variable_data": "Vec" + "variable_data": "Vec", + "pieces": "u128" }, "CreateItemData": { "_enum": { @@ -102,27 +80,21 @@ "CollectionId": "u32", "TokenId": "u32", "ChainLimits": { - "collection_numbers_limit": "u32", - "account_token_ownership_limit": "u32", - "collections_admins_limit": "u64", - "custom_data_limit": "u32", - "nft_sponsor_timeout": "u32", - "fungible_sponsor_timeout": "u32", - "refungible_sponsor_timeout": "u32" + "CollectionNumbersLimit": "u32", + "AccountTokenOwnershipLimit": "u32", + "CollectionAdminsLimit": "u64", + "CustomDataLimit": "u32", + "NftSponsorTimeout": "u32", + "FungibleSponsorTimeout": "u32", + "RefungibleSponsorTimeout": "u32", + "OffchainSchemaLimit": "u32", + "VariableOnChainSchemaLimit": "u32", + "ConstOnChainSchemaLimit": "u32" }, "CollectionLimits": { "AccountTokenOwnershipLimit": "u32", "SponsoredMintSize": "u32", "TokenLimit": "u32", "SponsorTimeout": "u32" - }, - "AccountInfo": "AccountInfoWithProviders", - "AccountInfoWithProviders": { - "nonce": "Index", - "consumers": "RefCount", - "providers": "RefCount", - "data": "AccountData" } - - } - \ No newline at end of file +} \ No newline at end of file --- /dev/null +++ b/tests/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "mocha.enabled": true +} \ No newline at end of file --- a/tests/README.md +++ b/tests/README.md @@ -2,8 +2,8 @@ ## How to run -1. Run `npm install`. +1. Run `yarn install`. 2. Setup a test node. You can do it using `docker-compose up -d` in parent directory. 3. Optional step - configure tests with env variables or by editing [configuration file](src/config.ts). -4. Run `npm test`. +4. Run `yarn test`. --- a/tests/package.json +++ b/tests/package.json @@ -22,10 +22,13 @@ "load": "mocha --timeout 9999999 -r ts-node/register ./**/*.load.ts", "testAddCollectionAdmin": "mocha --timeout 9999999 -r ts-node/register ./**/addCollectionAdmin.test.ts", "testSetSchemaVersion": "mocha --timeout 9999999 -r ts-node/register ./**/setSchemaVersion.test.ts", + "testSetVariableMetaData": "mocha --timeout 9999999 -r ts-node/register ./**/setVariableMetaData.test.ts", "testSetCollectionLimits": "mocha --timeout 9999999 -r ts-node/register ./**/setCollectionLimits.test.ts", "testRemoveCollectionAdmin": "mocha --timeout 9999999 -r ts-node/register ./**/removeCollectionAdmin.test.ts", + "testRemoveFromWhiteList": "mocha --timeout 9999999 -r ts-node/register ./**/removeFromWhiteList.test.ts", "testConnection": "mocha --timeout 9999999 -r ts-node/register ./**/connection.test.ts", "testCollection": "mocha --timeout 9999999 -r ts-node/register ./**/createCollection.test.ts", + "testCreateMultipleItems": "mocha --timeout 9999999 -r ts-node/register ./**/createMultipleItems.test.ts", "testApprove": "mocha --timeout 9999999 -r ts-node/register ./**/approve.test.ts", "testTransferFrom": "mocha --timeout 9999999 -r ts-node/register ./**/transferFrom.test.ts", "testCreateCollection": "mocha --timeout 9999999 -r ts-node/register ./**/createCollection.test.ts", @@ -33,7 +36,10 @@ "testAddToContractWhiteList": "mocha --timeout 9999999 -r ts-node/register ./**/addToContractWhiteList.test.ts", "testTransfer": "mocha --timeout 9999999 -r ts-node/register ./**/transfer.test.ts", "testBurnItem": "mocha --timeout 9999999 -r ts-node/register ./**/burnItem.test.ts", - "testCreditFeesToTreasury": "mocha --timeout 9999999 -r ts-node/register ./**/creditFeesToTreasury.test.ts" + "testSetMintPermission": "mocha --timeout 9999999 -r ts-node/register ./**/setMintPermission.test.ts", + "testCreditFeesToTreasury": "mocha --timeout 9999999 -r ts-node/register ./**/creditFeesToTreasury.test.ts", + "testEnableContractSponsoring": "mocha --timeout 9999999 -r ts-node/register ./**/enableContractSponsoring.test.ts", + "testSetContractSponsoringRateLimit": "mocha --timeout 9999999 -r ts-node/register ./**/setContractSponsoringRateLimit.test.ts" }, "author": "", "license": "SEE LICENSE IN ../LICENSE", --- a/tests/src/addCollectionAdmin.test.ts +++ b/tests/src/addCollectionAdmin.test.ts @@ -1,4 +1,9 @@ -import { ApiPromise } from '@polkadot/api'; +// +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. +// + +import { ApiPromise } from '@polkadot/api'; import BN from 'bn.js'; import chai from 'chai'; import chaiAsPromised from 'chai-as-promised'; @@ -98,7 +103,7 @@ }); }); - it('Add an admin to a collection that has reached the maximum number of admins limit', async () => { + it.only('Add an admin to a collection that has reached the maximum number of admins limit', async () => { await usingApi(async (api: ApiPromise) => { const Alice = privateKey('//Alice'); const accounts = [ @@ -112,18 +117,18 @@ ]; const collectionId = await createCollectionExpectSuccess(); - const chainLimit = await api.query.nft.chainLimit() as unknown as { collections_admins_limit: BN }; - const chainLimitNumber = chainLimit.collections_admins_limit.toNumber(); - expect(chainLimitNumber).to.be.equal(5); + const chainLimit = await api.query.nft.chainLimit() as unknown as { CollectionAdminsLimit: BN }; + const chainAdminLimit = chainLimit.CollectionAdminsLimit.toNumber(); + expect(chainAdminLimit).to.be.equal(5); - for (let i = 0; i < chainLimitNumber; i++) { + for (let i = 0; i < chainAdminLimit; i++) { const changeAdminTx = api.tx.nft.addCollectionAdmin(collectionId, accounts[i]); await submitTransactionAsync(Alice, changeAdminTx); const adminListAfterAddAdmin: any = (await api.query.nft.adminList(collectionId)); expect(adminListAfterAddAdmin).to.be.contains(accounts[i]); } - const tx = api.tx.nft.addCollectionAdmin(collectionId, accounts[chainLimitNumber]); + const tx = api.tx.nft.addCollectionAdmin(collectionId, accounts[chainAdminLimit]); await expect(submitTransactionExpectFailAsync(Alice, tx)).to.be.rejected; }); }); --- a/tests/src/addToContractWhiteList.test.ts +++ b/tests/src/addToContractWhiteList.test.ts @@ -1,3 +1,8 @@ +// +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. +// + import chai from "chai"; import chaiAsPromised from 'chai-as-promised'; import usingApi, { submitTransactionAsync, submitTransactionExpectFailAsync } from "./substrate/substrate-api"; --- a/tests/src/addToWhiteList.test.ts +++ b/tests/src/addToWhiteList.test.ts @@ -1,3 +1,8 @@ +// +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. +// + import { IKeyringPair } from '@polkadot/types/types'; import chai from 'chai'; import chaiAsPromised from 'chai-as-promised'; --- a/tests/src/approve.test.ts +++ b/tests/src/approve.test.ts @@ -34,7 +34,7 @@ await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob); // reFungible const reFungibleCollectionId = - await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}}); + await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible'); await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob); }); @@ -56,7 +56,7 @@ await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 0); // reFungible const reFungibleCollectionId = - await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}}); + await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible'); await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob, 1); await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob, 0); @@ -95,7 +95,7 @@ await approveExpectFail(fungibleCollectionId, 1, Alice, Bob); // reFungible const reFungibleCollectionId = - await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}}); + await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); await destroyCollectionExpectSuccess(reFungibleCollectionId); await approveExpectFail(reFungibleCollectionId, 1, Alice, Bob); }); @@ -113,7 +113,7 @@ await approveExpectFail(fungibleCollectionId, 2, Alice, Bob); // reFungible const reFungibleCollectionId = - await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}}); + await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); await approveExpectFail(reFungibleCollectionId, 2, Alice, Bob); }); }); @@ -132,7 +132,7 @@ await approveExpectFail(fungibleCollectionId, newFungibleTokenId, Bob, Alice); // reFungible const reFungibleCollectionId = - await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}}); + await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible'); await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, Bob, Alice); }); --- a/tests/src/burnItem.test.ts +++ b/tests/src/burnItem.test.ts @@ -1,3 +1,8 @@ +// +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. +// + import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from './substrate/substrate-api'; import { Keyring } from "@polkadot/api"; import { IKeyringPair } from "@polkadot/types/types"; @@ -70,7 +75,7 @@ }); it('Burn item in ReFungible collection', async () => { const createMode = 'ReFungible'; - const collectionId = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 2 }}); + const collectionId = await createCollectionExpectSuccess({mode: {type: createMode }}); const tokenId = await createItemExpectSuccess(alice, collectionId, createMode); await usingApi(async (api) => { @@ -91,7 +96,7 @@ it('Burn owned portion of item in ReFungible collection', async () => { const createMode = 'ReFungible'; - const collectionId = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 2 }}); + const collectionId = await createCollectionExpectSuccess({mode: {type: createMode}}); const tokenId = await createItemExpectSuccess(alice, collectionId, createMode); await usingApi(async (api) => { @@ -107,7 +112,7 @@ const tx = api.tx.nft.burnItem(collectionId, tokenId, 0); const events2 = await submitTransactionAsync(bob, tx); const result2 = getGenericResult(events2); - + // Get balances const balance: any = (await api.query.nft.reFungibleItemList(collectionId, tokenId)).toJSON(); // console.log(balance); --- a/tests/src/change-collection-owner.test.ts +++ b/tests/src/change-collection-owner.test.ts @@ -1,4 +1,9 @@ -import chai from 'chai'; +// +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. +// + +import chai from 'chai'; import chaiAsPromised from 'chai-as-promised'; import privateKey from './substrate/privateKey'; import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from "./substrate/substrate-api"; --- a/tests/src/confirmSponsorship.test.ts +++ b/tests/src/confirmSponsorship.test.ts @@ -115,7 +115,7 @@ }); it('ReFungible: Transfer fees are paid by the sponsor after confirmation', async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0 }}); + const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); await setCollectionSponsorExpectSuccess(collectionId, bob.address); await confirmSponsorshipExpectSuccess(collectionId, '//Bob'); @@ -247,7 +247,7 @@ }); it('ReFungible: Sponsoring is rate limited', async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0 }}); + const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); await setCollectionSponsorExpectSuccess(collectionId, bob.address); await confirmSponsorshipExpectSuccess(collectionId, '//Bob'); --- a/tests/src/contracts.test.ts +++ b/tests/src/contracts.test.ts @@ -1,3 +1,8 @@ +// +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. +// + import chai from "chai"; import chaiAsPromised from 'chai-as-promised'; import usingApi, { submitTransactionAsync, submitTransactionExpectFailAsync } from "./substrate/substrate-api"; --- a/tests/src/createCollection.test.ts +++ b/tests/src/createCollection.test.ts @@ -28,7 +28,7 @@ await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); }); it('Create new ReFungible collection', async () => { - await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}}); + await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); }); }); --- a/tests/src/createItem.test.ts +++ b/tests/src/createItem.test.ts @@ -1,3 +1,8 @@ +// +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. +// + import { default as usingApi } from './substrate/substrate-api'; import { Keyring } from "@polkadot/api"; import { IKeyringPair } from "@polkadot/types/types"; @@ -28,7 +33,7 @@ }); it('Create new item in ReFungible collection', async () => { const createMode = 'ReFungible'; - const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0}}); + const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode}}); await createItemExpectSuccess(alice, newCollectionID, createMode); }); }); --- a/tests/src/createMultipleItems.test.ts +++ b/tests/src/createMultipleItems.test.ts @@ -2,68 +2,177 @@ // This file is subject to the terms and conditions defined in // file 'LICENSE', which is part of this source code package. // +import { ApiPromise } from '@polkadot/api'; +import BN from 'bn.js'; +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import privateKey from './substrate/privateKey'; +import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from './substrate/substrate-api'; +import { + createCollectionExpectSuccess, + destroyCollectionExpectSuccess, + IReFungibleTokenDataType, +} from './util/helpers'; -import { assert } from 'chai'; -import { alicesPublicKey } from './accounts'; -import privateKey from './substrate/privateKey'; -import usingApi from './substrate/substrate-api'; -import waitNewBlocks from './substrate/wait-new-blocks'; +chai.use(chaiAsPromised); +const expect = chai.expect; -const idCollection = 12; +interface ITokenDataType { + Owner: number[]; + ConstData: number[]; + VariableData: number[]; +} -describe.skip('integration test: ext. createMultipleItems():', () => { - it('Create two NFT tokens in active NFT collection', async () => { - await usingApi(async (api) => { - const AitemListIndex = await api.query.nft.itemListIndex(idCollection); - console.log(`itemListIndex count (before): ${AitemListIndex}`); - const args = ['NFT', 'NFT']; - const alicePrivateKey = privateKey('//Alice'); - const createMultipleItems = await api.tx.nft - .createMultipleItems(idCollection, alicesPublicKey, args) - .signAndSend(alicePrivateKey); - // tslint:disable-next-line: no-unused-expression - assert.exists(createMultipleItems, 'createMultipleItems is neither `null` or `undefined`'); - console.log(`Ext. createMultipleItems submitted with hash: ${createMultipleItems}`); - await waitNewBlocks(api); - const BitemListIndex = await api.query.nft.itemListIndex(idCollection); - console.log(`itemListIndex count (after): ${BitemListIndex}`); - if (BitemListIndex === AitemListIndex) { assert.fail('Corret token not added in collection!'); } +describe('Integration Test createMultipleItems(collection_id, owner, items_data):', () => { + it('Create 0x31, 0x32, 0x33 items in active NFT collection and verify tokens data in chain', async () => { + await usingApi(async (api: ApiPromise) => { + const collectionId = await createCollectionExpectSuccess(); + const itemsListIndexBefore = await api.query.nft.itemListIndex(collectionId) as unknown as BN; + expect(itemsListIndexBefore.toNumber()).to.be.equal(0); + const Alice = privateKey('//Alice'); + const args = [{ nft: ['0x31', '0x31'] }, { nft: ['0x32', '0x32'] }, { nft: ['0x33', '0x33'] }]; + const createMultipleItemsTx = await api.tx.nft + .createMultipleItems(collectionId, Alice.address, args); + await submitTransactionAsync(Alice, createMultipleItemsTx); + const itemsListIndexAfter = await api.query.nft.itemListIndex(collectionId) as unknown as BN; + expect(itemsListIndexAfter.toNumber()).to.be.equal(3); + const token1Data = await api.query.nft.nftItemList(collectionId, 1) as unknown as ITokenDataType; + const token2Data = await api.query.nft.nftItemList(collectionId, 2) as unknown as ITokenDataType; + const token3Data = await api.query.nft.nftItemList(collectionId, 3) as unknown as ITokenDataType; + + expect(token1Data.Owner.toString()).to.be.equal(Alice.address); + expect(token2Data.Owner.toString()).to.be.equal(Alice.address); + expect(token3Data.Owner.toString()).to.be.equal(Alice.address); + + expect(token1Data.ConstData.toString()).to.be.equal('0x31'); + expect(token2Data.ConstData.toString()).to.be.equal('0x32'); + expect(token3Data.ConstData.toString()).to.be.equal('0x33'); + + expect(token1Data.VariableData.toString()).to.be.equal('0x31'); + expect(token2Data.VariableData.toString()).to.be.equal('0x32'); + expect(token3Data.VariableData.toString()).to.be.equal('0x33'); }); }); - it('(!negative test!) Create two Fungible tokens in active NFT collection', async () => { - await usingApi(async (api) => { - const AitemListIndex = await api.query.nft.itemListIndex(idCollection); - console.log(`itemListIndex count (before): ${AitemListIndex}`); - const args = ['Fungible', 'Fungible']; - const alicePrivateKey = privateKey('//Alice'); - const createMultipleItems = await api.tx.nft - .createMultipleItems(idCollection, alicesPublicKey, args) - .signAndSend(alicePrivateKey); - // tslint:disable-next-line: no-unused-expression - assert.exists(createMultipleItems, 'createMultipleItems is neither `null` or `undefined`'); - console.log(`Ext. createMultipleItems submitted with hash: ${createMultipleItems}`); - await waitNewBlocks(api); - const BitemListIndex = await api.query.nft.itemListIndex(idCollection); - console.log(`itemListIndex count (after): ${BitemListIndex}`); - if (BitemListIndex > AitemListIndex) { assert.fail('Incorrect token added in collection!'); } + + it('Create 0x31, 0x32, 0x33 items in active ReFungible collection and verify tokens data in chain', async () => { + await usingApi(async (api: ApiPromise) => { + const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); + const itemsListIndexBefore = await api.query.nft.itemListIndex(collectionId) as unknown as BN; + expect(itemsListIndexBefore.toNumber()).to.be.equal(0); + const Alice = privateKey('//Alice'); + const args = [ + {refungible: {const_data: [0x31], variable_data: [0x31], pieces: 1}}, + {refungible: {const_data: [0x32], variable_data: [0x32], pieces: 1}}, + {refungible: {const_data: [0x33], variable_data: [0x33], pieces: 1}}, + ]; + const createMultipleItemsTx = await api.tx.nft + .createMultipleItems(collectionId, Alice.address, args); + await submitTransactionAsync(Alice, createMultipleItemsTx); + const itemsListIndexAfter = await api.query.nft.itemListIndex(collectionId) as unknown as BN; + expect(itemsListIndexAfter.toNumber()).to.be.equal(3); + const token1Data = await api.query.nft.reFungibleItemList(collectionId, 1) as unknown as IReFungibleTokenDataType; + const token2Data = await api.query.nft.reFungibleItemList(collectionId, 2) as unknown as IReFungibleTokenDataType; + const token3Data = await api.query.nft.reFungibleItemList(collectionId, 3) as unknown as IReFungibleTokenDataType; + + expect(token1Data.Owner[0].Owner.toString()).to.be.equal(Alice.address); + expect(token1Data.Owner[0].Fraction.toNumber()).to.be.equal(1); + + expect(token2Data.Owner[0].Owner.toString()).to.be.equal(Alice.address); + expect(token2Data.Owner[0].Fraction.toNumber()).to.be.equal(1); + + expect(token3Data.Owner[0].Owner.toString()).to.be.equal(Alice.address); + expect(token3Data.Owner[0].Fraction.toNumber()).to.be.equal(1); + + expect(token1Data.ConstData.toString()).to.be.equal('0x31'); + expect(token2Data.ConstData.toString()).to.be.equal('0x32'); + expect(token3Data.ConstData.toString()).to.be.equal('0x33'); + + expect(token1Data.VariableData.toString()).to.be.equal('0x31'); + expect(token2Data.VariableData.toString()).to.be.equal('0x32'); + expect(token3Data.VariableData.toString()).to.be.equal('0x33'); }); }); - it('(!negative test!) Create two ReFungible tokens in active NFT collection', async () => { - await usingApi(async (api) => { - const AitemListIndex = await api.query.nft.itemListIndex(idCollection); - console.log(`itemListIndex count (before): ${AitemListIndex}`); - const args = ['ReFungible', 'ReFungible']; - const alicePrivateKey = privateKey('//Alice'); - const createMultipleItems = await api.tx.nft - .createMultipleItems(idCollection, alicesPublicKey, args) - .signAndSend(alicePrivateKey); - // tslint:disable-next-line: no-unused-expression - assert.exists(createMultipleItems, 'createMultipleItems is neither `null` or `undefined`'); - console.log(`Ext. createMultipleItems submitted with hash: ${createMultipleItems}`); - await waitNewBlocks(api); - const BitemListIndex = await api.query.nft.itemListIndex(idCollection); - console.log(`itemListIndex count (after): ${BitemListIndex}`); - if (BitemListIndex > AitemListIndex) { assert.fail('Incorrect token added in collection!'); } +}); + +describe('Negative Integration Test createMultipleItems(collection_id, owner, items_data):', () => { + it('Create token with not existing type', async () => { + await usingApi(async (api: ApiPromise) => { + const collectionId = await createCollectionExpectSuccess(); + const Alice = privateKey('//Alice'); + try { + const args = [{ invalid: null }, { invalid: null }, { invalid: null }]; + const createMultipleItemsTx = await api.tx.nft + .createMultipleItems(collectionId, Alice.address, args); + await expect(submitTransactionExpectFailAsync(Alice, createMultipleItemsTx)).to.be.rejected; + } catch (e) { + // tslint:disable-next-line:no-unused-expression + expect(e).to.be.exist; + } + }); + }); + + it('Create token in not existing collection', async () => { + await usingApi(async (api: ApiPromise) => { + const collectionId = parseInt((await api.query.nft.createdCollectionCount()).toString()) + 1; + const Alice = privateKey('//Alice'); + const createMultipleItemsTx = await api.tx.nft + .createMultipleItems(collectionId, Alice.address, ['NFT', 'NFT', 'NFT']); + await expect(submitTransactionExpectFailAsync(Alice, createMultipleItemsTx)).to.be.rejected; + }); + }); + + it('Create NFT and Re-fungible tokens that has reached the maximum data limit', async () => { + await usingApi(async (api: ApiPromise) => { + // NFT + const collectionId = await createCollectionExpectSuccess(); + const Alice = privateKey('//Alice'); + const args = [ + { nft: ['A'.repeat(2049), 'A'.repeat(2049)] }, + { nft: ['B'.repeat(2049), 'B'.repeat(2049)] }, + { nft: ['C'.repeat(2049), 'C'.repeat(2049)] }, + ]; + const createMultipleItemsTx = await api.tx.nft + .createMultipleItems(collectionId, Alice.address, args); + await expect(submitTransactionExpectFailAsync(Alice, createMultipleItemsTx)).to.be.rejected; + + // ReFungible + const collectionIdReFungible = + await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); + const argsReFungible = [ + { ReFungible: ['1'.repeat(2049), '1'.repeat(2049)] }, + { ReFungible: ['2'.repeat(2049), '2'.repeat(2049)] }, + { ReFungible: ['3'.repeat(2049), '3'.repeat(2049)] }, + ]; + const createMultipleItemsTxFungible = await api.tx.nft + .createMultipleItems(collectionIdReFungible, Alice.address, argsReFungible); + await expect(submitTransactionExpectFailAsync(Alice, createMultipleItemsTxFungible)).to.be.rejected; + }); + }); + + it('Create tokens with different types', async () => { + await usingApi(async (api: ApiPromise) => { + const collectionId = await createCollectionExpectSuccess(); + const Alice = privateKey('//Alice'); + const createMultipleItemsTx = await api.tx.nft + .createMultipleItems(collectionId, Alice.address, ['NFT', 'Fungible', 'ReFungible']); + await expect(submitTransactionExpectFailAsync(Alice, createMultipleItemsTx)).to.be.rejected; + // garbage collection :-D + await destroyCollectionExpectSuccess(collectionId); + }); + }); + + it('Create tokens with different data limits <> maximum data limit', async () => { + await usingApi(async (api: ApiPromise) => { + const collectionId = await createCollectionExpectSuccess(); + const Alice = privateKey('//Alice'); + const args = [ + { nft: ['A', 'A'] }, + { nft: ['B', 'B'.repeat(2049)] }, + { nft: ['C'.repeat(2049), 'C'] }, + ]; + const createMultipleItemsTx = await api.tx.nft + .createMultipleItems(collectionId, Alice.address, args); + await expect(submitTransactionExpectFailAsync(Alice, createMultipleItemsTx)).to.be.rejected; }); }); }); --- a/tests/src/destroyCollection.test.ts +++ b/tests/src/destroyCollection.test.ts @@ -1,12 +1,14 @@ +// +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. +// + import chai from 'chai'; import chaiAsPromised from 'chai-as-promised'; -import { default as usingApi, submitTransactionAsync } from "./substrate/substrate-api"; -import { createCollectionExpectSuccess, createCollectionExpectFailure, destroyCollectionExpectSuccess, destroyCollectionExpectFailure } from "./util/helpers"; -import type { AccountId, EventRecord } from '@polkadot/types/interfaces'; -import privateKey from './substrate/privateKey'; +import { default as usingApi } from "./substrate/substrate-api"; +import { createCollectionExpectSuccess, destroyCollectionExpectSuccess, destroyCollectionExpectFailure } from "./util/helpers"; chai.use(chaiAsPromised); -const expect = chai.expect; describe('integration test: ext. destroyCollection():', () => { it('NFT collection can be destroyed', async () => { @@ -18,7 +20,7 @@ await destroyCollectionExpectSuccess(collectionId); }); it('ReFungible collection can be destroyed', async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}}); + const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); await destroyCollectionExpectSuccess(collectionId); }); }); --- /dev/null +++ b/tests/src/enableContractSponsoring.test.ts @@ -0,0 +1,89 @@ +// +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. +// + +import { IKeyringPair } from '@polkadot/types/types'; +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import privateKey from './substrate/privateKey'; +import usingApi from './substrate/substrate-api'; +import { deployFlipper, getFlipValue, toggleFlipValueExpectSuccess } from './util/contracthelpers'; +import { + enableContractSponsoringExpectFailure, + enableContractSponsoringExpectSuccess, + findUnusedAddress, + setContractSponsoringRateLimitExpectSuccess, +} from './util/helpers'; + +chai.use(chaiAsPromised); +const expect = chai.expect; + +describe('Integration Test enableContractSponsoring', () => { + it('ensure tx fee is paid from endowment', async () => { + await usingApi(async (api) => { + const user = await findUnusedAddress(api); + + const [flipper, deployer] = await deployFlipper(api); + await enableContractSponsoringExpectSuccess(deployer, flipper.address, true); + await setContractSponsoringRateLimitExpectSuccess(deployer, flipper.address, 1); + await toggleFlipValueExpectSuccess(user, flipper); + + expect(await getFlipValue(flipper, deployer)).to.be.false; + }); + }); + + it('ensure it can be enabled twice', async () => { + await usingApi(async (api) => { + const [flipper, deployer] = await deployFlipper(api); + + await enableContractSponsoringExpectSuccess(deployer, flipper.address, true); + await enableContractSponsoringExpectSuccess(deployer, flipper.address, true); + }); + }); + + it('ensure it can be disabled twice', async () => { + await usingApi(async (api) => { + const [flipper, deployer] = await deployFlipper(api); + + await enableContractSponsoringExpectSuccess(deployer, flipper.address, true); + await enableContractSponsoringExpectSuccess(deployer, flipper.address, false); + await enableContractSponsoringExpectSuccess(deployer, flipper.address, false); + }); + }); + + it('ensure it can be re-enabled', async () => { + await usingApi(async (api) => { + const [flipper, deployer] = await deployFlipper(api); + + await enableContractSponsoringExpectSuccess(deployer, flipper.address, true); + await enableContractSponsoringExpectSuccess(deployer, flipper.address, false); + await enableContractSponsoringExpectSuccess(deployer, flipper.address, true); + }); + }); + +}); + +describe('Negative Integration Test enableContractSponsoring', () => { + let alice: IKeyringPair; + + before(async () => { + alice = privateKey('//Alice'); + }); + + it('fails when called for non-contract address', async () => { + await usingApi(async (api) => { + const user = await findUnusedAddress(api); + + await enableContractSponsoringExpectFailure(alice, user.address, true); + }); + }); + + it('fails when called by non-owning user', async () => { + await usingApi(async (api) => { + const [flipper, _] = await deployFlipper(api); + + await enableContractSponsoringExpectFailure(alice, flipper.address, true); + }); + }); +}); --- a/tests/src/removeCollectionAdmin.test.ts +++ b/tests/src/removeCollectionAdmin.test.ts @@ -1,5 +1,9 @@ +// +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. +// + import { ApiPromise } from '@polkadot/api'; -import BN from 'bn.js'; import chai from 'chai'; import chaiAsPromised from 'chai-as-promised'; import privateKey from './substrate/privateKey'; --- /dev/null +++ b/tests/src/removeFromWhiteList.test.ts @@ -0,0 +1,89 @@ +// +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. +// + +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import { default as usingApi } from './substrate/substrate-api'; +import { + createCollectionExpectSuccess, + destroyCollectionExpectSuccess, + enableWhiteListExpectSuccess, + addToWhiteListExpectSuccess, + removeFromWhiteListExpectSuccess, + isWhitelisted, + findNotExistingCollection, + removeFromWhiteListExpectFailure, + disableWhiteListExpectSuccess, +} from './util/helpers'; +import { IKeyringPair } from '@polkadot/types/types'; +import privateKey from './substrate/privateKey'; + +chai.use(chaiAsPromised); +const expect = chai.expect; + +describe('Integration Test removeFromWhiteList', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingApi(async (api) => { + alice = privateKey('//Alice'); + bob = privateKey('//Bob'); + }); + }); + + it('ensure bob is not in whitelist after removal', async () => { + await usingApi(async () => { + const collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } }); + await enableWhiteListExpectSuccess(alice, collectionId); + await addToWhiteListExpectSuccess(alice, collectionId, bob.address); + + await removeFromWhiteListExpectSuccess(alice, collectionId, bob.address); + expect(await isWhitelisted(collectionId, bob.address)).to.be.false; + }); + }); + + it('allows removal from collection with unset whitelist status', async () => { + await usingApi(async () => { + const collectionWithoutWhitelistId = await createCollectionExpectSuccess(); + await enableWhiteListExpectSuccess(alice, collectionWithoutWhitelistId); + await addToWhiteListExpectSuccess(alice, collectionWithoutWhitelistId, bob.address); + await disableWhiteListExpectSuccess(alice, collectionWithoutWhitelistId); + + await removeFromWhiteListExpectSuccess(alice, collectionWithoutWhitelistId, bob.address); + }); + }); +}); + +describe('Negative Integration Test removeFromWhiteList', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingApi(async (api) => { + alice = privateKey('//Alice'); + bob = privateKey('//Bob'); + }); + }); + + it('fails on removal from not existing collection', async () => { + await usingApi(async (api) => { + const collectionId = await findNotExistingCollection(api); + + await removeFromWhiteListExpectFailure(alice, collectionId, bob.address); + }); + }); + + it('fails on removal from removed collection', async () => { + await usingApi(async () => { + const collectionId = await createCollectionExpectSuccess(); + await enableWhiteListExpectSuccess(alice, collectionId); + await addToWhiteListExpectSuccess(alice, collectionId, bob.address); + await destroyCollectionExpectSuccess(collectionId); + + await removeFromWhiteListExpectFailure(alice, collectionId, bob.address); + }); + }); +}); --- a/tests/src/setCollectionLimits.test.ts +++ b/tests/src/setCollectionLimits.test.ts @@ -1,3 +1,8 @@ +// +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. +// + // https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits import { ApiPromise, Keyring } from '@polkadot/api'; import { IKeyringPair } from '@polkadot/types/types'; --- a/tests/src/setCollectionSponsor.test.ts +++ b/tests/src/setCollectionSponsor.test.ts @@ -9,7 +9,6 @@ import { createCollectionExpectSuccess, setCollectionSponsorExpectSuccess, destroyCollectionExpectSuccess, setCollectionSponsorExpectFailure } from "./util/helpers"; import { Keyring } from "@polkadot/api"; import { IKeyringPair } from "@polkadot/types/types"; -import type { AccountId } from '@polkadot/types/interfaces'; chai.use(chaiAsPromised); const expect = chai.expect; @@ -34,7 +33,7 @@ await setCollectionSponsorExpectSuccess(collectionId, bob.address); }); it('Set ReFungible collection sponsor', async () => { - const collectionId = await createCollectionExpectSuccess({ mode: {type: 'ReFungible', decimalPoints: 0} }); + const collectionId = await createCollectionExpectSuccess({ mode: {type: 'ReFungible'} }); await setCollectionSponsorExpectSuccess(collectionId, bob.address); }); --- /dev/null +++ b/tests/src/setConstOnChainSchema.test.ts @@ -0,0 +1,96 @@ +// +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. +// + +import { Keyring } from '@polkadot/api'; +import { IKeyringPair } from '@polkadot/types/types'; +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from './substrate/substrate-api'; +import { + createCollectionExpectSuccess, + destroyCollectionExpectSuccess, +} from './util/helpers'; + +chai.use(chaiAsPromised); +const expect = chai.expect; + +let Alice: IKeyringPair; +let Bob: IKeyringPair; +let Shema: any; +let largeShema: any; + +before(async () => { + await usingApi(async (api) => { + const keyring = new Keyring({ type: 'sr25519' }); + Alice = keyring.addFromUri('//Alice'); + Bob = keyring.addFromUri('//Bob'); + Shema = '0x31'; + largeShema = new Array(4097).fill(0xff); + + }); +}); +describe('Integration Test ext. setConstOnChainSchema()', () => { + + it('Run extrinsic with parameters of the collection id, set the scheme', async () => { + await usingApi(async (api) => { + const collectionId = await createCollectionExpectSuccess(); + const collection: any = (await api.query.nft.collection(collectionId)); + expect(collection.Owner.toString()).to.be.eq(Alice.address); + const setShema = api.tx.nft.setConstOnChainSchema(collectionId, Shema); + await submitTransactionAsync(Alice, setShema); + }); + }); + + it('Checking collection data using the ConstOnChainSchema parameter', async () => { + await usingApi(async (api) => { + const collectionId = await createCollectionExpectSuccess(); + const setShema = api.tx.nft.setConstOnChainSchema(collectionId, Shema); + await submitTransactionAsync(Alice, setShema); + const collection: any = (await api.query.nft.collection(collectionId)); + expect(collection.ConstOnChainSchema.toString()).to.be.eq(Shema); + + }); + }); +}); + +describe('Negative Integration Test ext. setConstOnChainSchema()', () => { + + it('Set a non-existent collection', async () => { + await usingApi(async (api) => { + // tslint:disable-next-line: radix + const collectionId = parseInt((await api.query.nft.createdCollectionCount()).toString()) + 1; + const setShema = api.tx.nft.setConstOnChainSchema(collectionId, Shema); + await expect(submitTransactionExpectFailAsync(Alice, setShema)).to.be.rejected; + }); + }); + + it('Set a previously deleted collection', async () => { + await usingApi(async (api) => { + const collectionId = await createCollectionExpectSuccess(); + await destroyCollectionExpectSuccess(collectionId); + const setShema = api.tx.nft.setConstOnChainSchema(collectionId, Shema); + await expect(submitTransactionExpectFailAsync(Alice, setShema)).to.be.rejected; + }); + }); + + it('Set invalid data in schema (size too large:> 1024b)', async () => { + await usingApi(async (api) => { + const collectionId = await createCollectionExpectSuccess(); + const setShema = api.tx.nft.setConstOnChainSchema(collectionId, largeShema); + await expect(submitTransactionExpectFailAsync(Alice, setShema)).to.be.rejected; + }); + }); + + it('Execute method not on behalf of the collection owner', async () => { + await usingApi(async (api) => { + const collectionId = await createCollectionExpectSuccess(); + const collection: any = (await api.query.nft.collection(collectionId)); + expect(collection.Owner.toString()).to.be.eq(Alice.address); + const setShema = api.tx.nft.setConstOnChainSchema(collectionId, Shema); + await expect(submitTransactionExpectFailAsync(Bob, setShema)).to.be.rejected; + }); + }); + +}); --- /dev/null +++ b/tests/src/setContractSponsoringRateLimit.test.ts @@ -0,0 +1,67 @@ +// +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. +// + +import { IKeyringPair } from '@polkadot/types/types'; +import privateKey from './substrate/privateKey'; +import usingApi from './substrate/substrate-api'; +import waitNewBlocks from './substrate/wait-new-blocks'; +import { deployFlipper, toggleFlipValueExpectFailure, toggleFlipValueExpectSuccess } from './util/contracthelpers'; +import { + enableContractSponsoringExpectSuccess, + findUnusedAddress, + setContractSponsoringRateLimitExpectFailure, + setContractSponsoringRateLimitExpectSuccess, +} from './util/helpers'; + +describe('Integration Test setContractSponsoringRateLimit', () => { + it('ensure sponsored contract can\'t be called twice without pause for free', async () => { + await usingApi(async (api) => { + const user = await findUnusedAddress(api); + + const [flipper, deployer] = await deployFlipper(api); + await enableContractSponsoringExpectSuccess(deployer, flipper.address, true); + await setContractSponsoringRateLimitExpectSuccess(deployer, flipper.address, 10); + await toggleFlipValueExpectSuccess(user, flipper); + await toggleFlipValueExpectFailure(user, flipper); + }); + }); + + it('ensure sponsored contract can be called twice with pause for free', async () => { + await usingApi(async (api) => { + const user = await findUnusedAddress(api); + + const [flipper, deployer] = await deployFlipper(api); + await enableContractSponsoringExpectSuccess(deployer, flipper.address, true); + await setContractSponsoringRateLimitExpectSuccess(deployer, flipper.address, 1); + await toggleFlipValueExpectSuccess(user, flipper); + await waitNewBlocks(api, 1); + await toggleFlipValueExpectSuccess(user, flipper); + }); + }); +}); + +describe('Negative Integration Test setContractSponsoringRateLimit', () => { + let alice: IKeyringPair; + + before(async () => { + alice = privateKey('//Alice'); + }); + + it('fails when called for non-contract address', async () => { + await usingApi(async (api) => { + const user = await findUnusedAddress(api); + + await setContractSponsoringRateLimitExpectFailure(alice, user.address, 1); + }); + }); + + it('fails when called by non-owning user', async () => { + await usingApi(async (api) => { + const [flipper, _] = await deployFlipper(api); + + await setContractSponsoringRateLimitExpectFailure(alice, flipper.address, 1); + }); + }); +}); --- /dev/null +++ b/tests/src/setMintPermission.test.ts @@ -0,0 +1,103 @@ +// +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. +// + +import { IKeyringPair } from '@polkadot/types/types'; +import privateKey from './substrate/privateKey'; +import usingApi from './substrate/substrate-api'; +import { + addToWhiteListExpectSuccess, + createCollectionExpectSuccess, + createItemExpectFailure, + createItemExpectSuccess, + destroyCollectionExpectSuccess, + enableWhiteListExpectSuccess, + findNotExistingCollection, + setMintPermissionExpectFailure, + setMintPermissionExpectSuccess, +} from './util/helpers'; + +describe('Integration Test setMintPermission', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingApi(async () => { + alice = privateKey('//Alice'); + bob = privateKey('//Bob'); + }); + }); + + it('ensure white-listed non-privileged address can mint tokens', async () => { + await usingApi(async () => { + const collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } }); + await enableWhiteListExpectSuccess(alice, collectionId); + await setMintPermissionExpectSuccess(alice, collectionId, true); + await addToWhiteListExpectSuccess(alice, collectionId, bob.address); + + await createItemExpectSuccess(bob, collectionId, 'NFT'); + }); + }); + + it('can be enabled twice', async () => { + await usingApi(async () => { + const collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } }); + await setMintPermissionExpectSuccess(alice, collectionId, true); + await setMintPermissionExpectSuccess(alice, collectionId, true); + }); + }); + + it('can be disabled twice', async () => { + await usingApi(async () => { + const collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } }); + await setMintPermissionExpectSuccess(alice, collectionId, true); + await setMintPermissionExpectSuccess(alice, collectionId, false); + await setMintPermissionExpectSuccess(alice, collectionId, false); + }); + }); +}); + +describe('Negative Integration Test setMintPermission', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingApi(async () => { + alice = privateKey('//Alice'); + bob = privateKey('//Bob'); + }); + }); + + it('fails on not existing collection', async () => { + await usingApi(async (api) => { + const nonExistingCollection = await findNotExistingCollection(api); + await setMintPermissionExpectFailure(alice, nonExistingCollection, true); + }); + }); + + it('fails on removed collection', async () => { + await usingApi(async () => { + const removedCollectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } }); + await destroyCollectionExpectSuccess(removedCollectionId); + + await setMintPermissionExpectFailure(alice, removedCollectionId, true); + }); + }); + + it('fails when not collection owner tries to set mint status', async () => { + const collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } }); + await enableWhiteListExpectSuccess(alice, collectionId); + await setMintPermissionExpectFailure(bob, collectionId, true); + }); + + it('ensure non-white-listed non-privileged address can\'t mint tokens', async () => { + await usingApi(async () => { + const collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } }); + await enableWhiteListExpectSuccess(alice, collectionId); + await setMintPermissionExpectSuccess(alice, collectionId, true); + + await createItemExpectFailure(bob, collectionId, 'NFT'); + }); + }); +}); --- a/tests/src/setPublicAccessMode.test.ts +++ b/tests/src/setPublicAccessMode.test.ts @@ -1,3 +1,8 @@ +// +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. +// + // https://unique-network.readthedocs.io/en/latest/jsapi.html#setschemaversion import { ApiPromise, Keyring } from '@polkadot/api'; import { IKeyringPair } from '@polkadot/types/types'; --- a/tests/src/setSchemaVersion.test.ts +++ b/tests/src/setSchemaVersion.test.ts @@ -1,7 +1,11 @@ +// +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. +// + // https://unique-network.readthedocs.io/en/latest/jsapi.html#setschemaversion import { ApiPromise, Keyring } from '@polkadot/api'; import { IKeyringPair } from '@polkadot/types/types'; -import BN from 'bn.js'; import chai from 'chai'; import chaiAsPromised from 'chai-as-promised'; import usingApi, {submitTransactionAsync, submitTransactionExpectFailAsync} from './substrate/substrate-api'; @@ -98,12 +102,16 @@ it('execute setSchemaVersion with not correct schema version', async () => { await usingApi(async (api: ApiPromise) => { + const consoleError = console.error; + console.error = (message: string) => {}; try { tx = api.tx.nft.setSchemaVersion(collectionIdForTesting, 'Test'); await submitTransactionAsync(alice, tx); } catch (e) { // tslint:disable-next-line:no-unused-expression expect(e).to.be.exist; + } finally { + console.error = consoleError; } }); }); --- /dev/null +++ b/tests/src/setVariableMetaData.test.ts @@ -0,0 +1,109 @@ +// +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. +// + +import { IKeyringPair } from '@polkadot/types/types'; +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import privateKey from './substrate/privateKey'; +import usingApi from './substrate/substrate-api'; +import { + burnItemExpectSuccess, + createCollectionExpectSuccess, + createItemExpectSuccess, + destroyCollectionExpectSuccess, + findNotExistingCollection, + setVariableMetaDataExpectFailure, + setVariableMetaDataExpectSuccess, +} from './util/helpers'; + +chai.use(chaiAsPromised); +const expect = chai.expect; + +describe('Integration Test setVariableMetaData', () => { + const data = [1, 2, 254, 255]; + + let alice: IKeyringPair; + let collectionId: number; + let tokenId: number; + before(async () => { + await usingApi(async () => { + alice = privateKey('//Alice'); + collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } }); + tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT'); + }); + }); + + it('execute setVariableMetaData', async () => { + await setVariableMetaDataExpectSuccess(alice, collectionId, tokenId, data); + }); + + it('verify data was set', async () => { + await usingApi(async api => { + const item: any = await api.query.nft.nftItemList(collectionId, tokenId); + + expect(Array.from(item.VariableData)).to.deep.equal(Array.from(data)); + }); + }); +}); + +describe('Negative Integration Test setVariableMetaData', () => { + let data = [1]; + + let alice: IKeyringPair; + let bob: IKeyringPair; + + let validCollectionId: number; + let validTokenId: number; + + before(async () => { + await usingApi(async () => { + alice = privateKey('//Alice'); + bob = privateKey('//Bob'); + + validCollectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } }); + validTokenId = await createItemExpectSuccess(alice, validCollectionId, 'NFT'); + }); + }); + + it('fails on not existing collection id', async () => { + await usingApi(async api => { + let nonExistingCollectionId = await findNotExistingCollection(api); + await setVariableMetaDataExpectFailure(alice, nonExistingCollectionId, 1, data); + }); + }); + it('fails on removed collection id', async () => { + const removedCollectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } }); + const removedCollectionTokenId = await createItemExpectSuccess(alice, removedCollectionId, 'NFT'); + + await destroyCollectionExpectSuccess(removedCollectionId); + await setVariableMetaDataExpectFailure(alice, removedCollectionId, removedCollectionTokenId, data); + }); + it('fails on removed token', async () => { + const removedTokenCollectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } }); + const removedTokenId = await createItemExpectSuccess(alice, removedTokenCollectionId, 'NFT'); + await burnItemExpectSuccess(alice, removedTokenCollectionId, removedTokenId); + + await setVariableMetaDataExpectFailure(alice, removedTokenCollectionId, removedTokenId, data); + }); + it('fails on not existing token', async () => { + const nonExistingTokenId = validTokenId + 1; + + await setVariableMetaDataExpectFailure(alice, validCollectionId, nonExistingTokenId, data); + }); + it('fails on too long data', async () => { + const tooLongData = new Array(4097).fill(0xff); + + await setVariableMetaDataExpectFailure(alice, validCollectionId, validTokenId, tooLongData); + }); + it('fails on fungible token', async () => { + const fungibleCollectionId = await createCollectionExpectSuccess({ mode: { type: 'Fungible', decimalPoints: 0 } }); + const fungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible'); + + await setVariableMetaDataExpectFailure(alice, fungibleCollectionId, fungibleTokenId, data); + }); + it('fails on bad sender', async () => { + await setVariableMetaDataExpectFailure(bob, validCollectionId, validTokenId, data); + }); +}); --- /dev/null +++ b/tests/src/setVariableOnChainSchema.test.ts @@ -0,0 +1,96 @@ +// +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. +// + +import { Keyring } from '@polkadot/api'; +import { IKeyringPair } from '@polkadot/types/types'; +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from './substrate/substrate-api'; +import { + createCollectionExpectSuccess, + destroyCollectionExpectSuccess, +} from './util/helpers'; + +chai.use(chaiAsPromised); +const expect = chai.expect; + +let Alice: IKeyringPair; +let Bob: IKeyringPair; +let Schema: any; +let largeSchema: any; + +before(async () => { + await usingApi(async (api) => { + const keyring = new Keyring({ type: 'sr25519' }); + Alice = keyring.addFromUri('//Alice'); + Bob = keyring.addFromUri('//Bob'); + Schema = '0x31'; + largeSchema = new Array(4097).fill(0xff); + + }); +}); +describe('Integration Test ext. setVariableOnChainSchema()', () => { + + it('Run extrinsic with parameters of the collection id, set the scheme', async () => { + await usingApi(async (api) => { + const collectionId = await createCollectionExpectSuccess(); + const collection: any = (await api.query.nft.collection(collectionId)); + expect(collection.Owner.toString()).to.be.eq(Alice.address); + const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, Schema); + await submitTransactionAsync(Alice, setSchema); + }); + }); + + it('Checking collection data using the setVariableOnChainSchema parameter', async () => { + await usingApi(async (api) => { + const collectionId = await createCollectionExpectSuccess(); + const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, Schema); + await submitTransactionAsync(Alice, setSchema); + const collection: any = (await api.query.nft.collection(collectionId)); + expect(collection.VariableOnChainSchema.toString()).to.be.eq(Schema); + + }); + }); +}); + +describe('Negative Integration Test ext. setVariableOnChainSchema()', () => { + + it('Set a non-existent collection', async () => { + await usingApi(async (api) => { + // tslint:disable-next-line: radix + const collectionId = parseInt((await api.query.nft.createdCollectionCount()).toString()) + 1; + const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, Schema); + await expect(submitTransactionExpectFailAsync(Alice, setSchema)).to.be.rejected; + }); + }); + + it('Set a previously deleted collection', async () => { + await usingApi(async (api) => { + const collectionId = await createCollectionExpectSuccess(); + await destroyCollectionExpectSuccess(collectionId); + const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, Schema); + await expect(submitTransactionExpectFailAsync(Alice, setSchema)).to.be.rejected; + }); + }); + + it('Set invalid data in schema (size too large:> 1024b)', async () => { + await usingApi(async (api) => { + const collectionId = await createCollectionExpectSuccess(); + const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, largeSchema); + await expect(submitTransactionExpectFailAsync(Alice, setSchema)).to.be.rejected; + }); + }); + + it('Execute method not on behalf of the collection owner', async () => { + await usingApi(async (api) => { + const collectionId = await createCollectionExpectSuccess(); + const collection: any = (await api.query.nft.collection(collectionId)); + expect(collection.Owner.toString()).to.be.eq(Alice.address); + const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, Schema); + await expect(submitTransactionExpectFailAsync(Bob, setSchema)).to.be.rejected; + }); + }); + +}); --- a/tests/src/substrate/substrate-api.ts +++ b/tests/src/substrate/substrate-api.ts @@ -24,12 +24,17 @@ // TODO: Remove, this is temporary: Filter unneeded API output // (Jaco promised it will be removed in the next version) - // const consoleLog = console.log; - // console.log = (message: string) => { - // if (!(message.includes("API/INIT: Capabilities detected") || message.includes("2021-"))) { - // consoleLog(message); - // } - // }; + const consoleLog = console.log; + console.log = (message: string) => { + if (message.includes("API/INIT: Capabilities detected") || message.includes("2021-")) {} + else if (message.includes("StorageChangeSet:: WebSocket is not connected") || message.includes("2021-")) {} + else consoleLog(message); + }; + const consoleErr = console.error; + console.error = (message: string) => { + if (message.includes("StorageChangeSet:: WebSocket is not connected") || message.includes("2021-")) {} + else consoleErr(message); + }; try { await promisifySubstrate(api, async () => { @@ -40,7 +45,8 @@ })(); } finally { await api.disconnect(); - // console.log = consoleLog; + console.log = consoleLog; + console.error = consoleErr; } } @@ -115,11 +121,11 @@ await transaction.signAndSend(sender, ({ events = [], status }) => { const transactionStatus = getTransactionStatus(events, status); - console.log('transactionStatus', transactionStatus, 'events', events); + // console.log('transactionStatus', transactionStatus, 'events', events); - if (transactionStatus == TransactionStatus.Success) { + if (transactionStatus === TransactionStatus.Success) { resolve(events); - } else if (transactionStatus == TransactionStatus.Fail) { + } else if (transactionStatus === TransactionStatus.Fail) { reject(events); } }); --- a/tests/src/toggleContractWhiteList.test.ts +++ b/tests/src/toggleContractWhiteList.test.ts @@ -1,3 +1,8 @@ +// +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. +// + import chai from "chai"; import chaiAsPromised from 'chai-as-promised'; import usingApi, { submitTransactionAsync, submitTransactionExpectFailAsync } from "./substrate/substrate-api"; --- a/tests/src/transfer.test.ts +++ b/tests/src/transfer.test.ts @@ -63,7 +63,7 @@ }); }); - it('Create collection, balance transfers and check balance', async () => { + it('User can transfer owned token', async () => { await usingApi(async (api) => { const Alice = privateKey('//Alice'); const Bob = privateKey('//Bob'); @@ -77,10 +77,10 @@ await transferExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 1, 'Fungible'); // reFungible const reFungibleCollectionId = await - createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}}); + createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible'); await transferExpectSuccess(reFungibleCollectionId, - newReFungibleTokenId, Alice, Bob, 1, 'ReFungible'); + newReFungibleTokenId, Alice, Bob, 100, 'ReFungible'); }); }); }); @@ -119,7 +119,7 @@ await transferExpectFail(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 1, 'Fungible'); // reFungible const reFungibleCollectionId = await - createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}}); + createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible'); await destroyCollectionExpectSuccess(reFungibleCollectionId); await transferExpectFail(reFungibleCollectionId, @@ -134,7 +134,7 @@ await transferExpectFail(fungibleCollectionId, 2, Alice, Bob, 1, 'Fungible'); // reFungible const reFungibleCollectionId = await - createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}}); + createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); await transferExpectFail(reFungibleCollectionId, 2, Alice, Bob, 1, 'ReFungible'); }); @@ -151,7 +151,7 @@ await transferExpectFail(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 1, 'Fungible'); // reFungible const reFungibleCollectionId = await - createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}}); + createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible'); await burnItemExpectSuccess(Alice, reFungibleCollectionId, newReFungibleTokenId, 1); await transferExpectFail(reFungibleCollectionId, @@ -168,7 +168,7 @@ await transferExpectFail(fungibleCollectionId, newFungibleTokenId, Charlie, Bob, 1, 'Fungible'); // reFungible const reFungibleCollectionId = await - createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}}); + createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible'); await transferExpectFail(reFungibleCollectionId, newReFungibleTokenId, Charlie, Bob, 1, 'ReFungible'); --- a/tests/src/transferFrom.test.ts +++ b/tests/src/transferFrom.test.ts @@ -41,11 +41,11 @@ await transferFromExpectSuccess(fungibleCollectionId, newFungibleTokenId, Bob, Alice, Charlie, 1, 'Fungible'); // reFungible const reFungibleCollectionId = await - createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}}); + createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible'); - await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob); + await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob, 100); await transferFromExpectSuccess(reFungibleCollectionId, - newReFungibleTokenId, Bob, Alice, Charlie, 1, 'ReFungible'); + newReFungibleTokenId, Bob, Alice, Charlie, 100, 'ReFungible'); }); }); }); @@ -110,7 +110,7 @@ await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, Bob, Alice, Charlie, 1); // reFungible const reFungibleCollectionId = await - createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}}); + createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible'); await transferFromExpectFail(reFungibleCollectionId, newReFungibleTokenId, Bob, Alice, Charlie, 1); @@ -136,7 +136,7 @@ await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, Bob, Alice, Charlie, 2); // reFungible const reFungibleCollectionId = await - createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}}); + createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible'); await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob); await transferFromExpectFail(reFungibleCollectionId, @@ -149,7 +149,7 @@ const Alice = privateKey('//Alice'); const Bob = privateKey('//Bob'); const Charlie = privateKey('//Charlie'); - const Dave = privateKey('//DAVE'); + const Dave = privateKey('//Dave'); // nft const nftCollectionId = await createCollectionExpectSuccess(); const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT'); @@ -175,7 +175,7 @@ } // reFungible const reFungibleCollectionId = await - createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}}); + createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible'); try { await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, Dave, Bob); --- a/tests/src/types.ts +++ b/tests/src/types.ts @@ -1,3 +1,8 @@ +// +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. +// + import BN from 'bn.js'; export interface ICollectionInterface { --- a/tests/src/util/contracthelpers.ts +++ b/tests/src/util/contracthelpers.ts @@ -5,7 +5,7 @@ import chai from "chai"; import chaiAsPromised from 'chai-as-promised'; -import { submitTransactionAsync } from "../substrate/substrate-api"; +import { submitTransactionAsync, submitTransactionExpectFailAsync } from "../substrate/substrate-api"; import fs from "fs"; import { Abi, BlueprintPromise as Blueprint, CodePromise, ContractPromise as Contract } from "@polkadot/api-contract"; import { IKeyringPair } from "@polkadot/types/types"; @@ -14,7 +14,7 @@ chai.use(chaiAsPromised); const expect = chai.expect; import { BigNumber } from 'bignumber.js'; -import { findUnusedAddress } from '../util/helpers'; +import { findUnusedAddress, getGenericResult } from '../util/helpers'; const value = 0; const gasLimit = '200000000000'; @@ -92,6 +92,19 @@ return (result.result.asOk.data[0] == 0x00) ? false : true; } +export async function toggleFlipValueExpectSuccess(sender: IKeyringPair, contract: Contract) { + const tx = contract.tx.flip(value, gasLimit); + const events = await submitTransactionAsync(sender, tx); + const result = getGenericResult(events); + + expect(result.success).to.be.true; +} + +export async function toggleFlipValueExpectFailure(sender: IKeyringPair, contract: Contract) { + const tx = contract.tx.flip(value, gasLimit); + await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected; +} + function instantiateTransferContract(alice: IKeyringPair, blueprint: Blueprint) : Promise { return new Promise(async (resolve, reject) => { const unsub = await blueprint.tx --- a/tests/src/util/helpers.ts +++ b/tests/src/util/helpers.ts @@ -51,7 +51,7 @@ Value: BN; } -interface IReFungibleTokenDataType { +export interface IReFungibleTokenDataType { Owner: IReFungibleOwner[]; ConstData: number[]; VariableData: number[]; @@ -124,21 +124,6 @@ interface ReFungible { type: 'ReFungible'; - decimalPoints: number; -} - -interface Nft { - type: 'NFT' -} - -interface Fungible { - type: 'Fungible', - decimalPoints: number -} - -interface ReFungible { - type: 'ReFungible', - decimalPoints: number } type CollectionMode = Nft | Fungible | ReFungible | Invalid; @@ -174,7 +159,7 @@ } else if (mode.type === 'Fungible') { modeprm = {fungible: mode.decimalPoints}; } else if (mode.type === 'ReFungible') { - modeprm = {refungible: mode.decimalPoints}; + modeprm = {refungible: null}; } else if (mode.type === 'Invalid') { modeprm = {invalid: null}; } @@ -216,7 +201,7 @@ } else if (mode.type === 'Fungible') { modeprm = {fungible: mode.decimalPoints}; } else if (mode.type === 'ReFungible') { - modeprm = {refungible: mode.decimalPoints}; + modeprm = {refungible: null}; } else if (mode.type === 'Invalid') { modeprm = {invalid: null}; } @@ -253,6 +238,12 @@ return unused; } +export async function findNotExistingCollection(api: ApiPromise): Promise { + const totalNumber = parseInt((await api.query.nft.createdCollectionCount()).toString(), 10) as unknown as number; + const newCollection: number = totalNumber + 1; + return newCollection; +} + function getDestroyResult(events: EventRecord[]): boolean { let success: boolean = false; events.forEach(({ phase, event: { data, method, section } }) => { @@ -379,6 +370,63 @@ }); } +export async function enableContractSponsoringExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, enable: boolean) { + await usingApi(async (api) => { + const tx = api.tx.nft.enableContractSponsoring(contractAddress, enable); + const events = await submitTransactionAsync(sender, tx); + const result = getGenericResult(events); + + expect(result.success).to.be.true; + }); +} + +export async function enableContractSponsoringExpectFailure(sender: IKeyringPair, contractAddress: AccountId | string, enable: boolean) { + await usingApi(async (api) => { + const tx = api.tx.nft.enableContractSponsoring(contractAddress, enable); + const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected; + const result = getGenericResult(events); + + expect(result.success).to.be.false; + }); +} + +export async function setContractSponsoringRateLimitExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, rateLimit: number) { + await usingApi(async (api) => { + const tx = api.tx.nft.setContractSponsoringRateLimit(contractAddress, rateLimit); + const events = await submitTransactionAsync(sender, tx); + const result = getGenericResult(events); + + expect(result.success).to.be.true; + }); +} + +export async function setContractSponsoringRateLimitExpectFailure(sender: IKeyringPair, contractAddress: AccountId | string, rateLimit: number) { + await usingApi(async (api) => { + const tx = api.tx.nft.setContractSponsoringRateLimit(contractAddress, rateLimit); + const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected; + const result = getGenericResult(events); + + expect(result.success).to.be.false; + }); +} + +export async function setVariableMetaDataExpectSuccess(sender: IKeyringPair, collectionId: number, itemId: number, data: number[]) { + await usingApi(async (api) => { + const tx = api.tx.nft.setVariableMetaData(collectionId, itemId, '0x' + Buffer.from(data).toString('hex')); + const events = await submitTransactionAsync(sender, tx); + const result = getGenericResult(events); + + expect(result.success).to.be.true; + }); +} + +export async function setVariableMetaDataExpectFailure(sender: IKeyringPair, collectionId: number, itemId: number, data: number[]) { + await usingApi(async (api) => { + const tx = api.tx.nft.setVariableMetaData(collectionId, itemId, '0x' + Buffer.from(data).toString('hex')); + await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected; + }); +} + export interface CreateFungibleData extends Struct { readonly value: u128; } @@ -440,7 +488,7 @@ } const transferFromTx = await api.tx.nft.transferFrom( accountFrom.address, accountTo.address, collectionId, tokenId, value); - const events = await submitTransactionAsync(accountFrom, transferFromTx); + const events = await submitTransactionAsync(accountApproved, transferFromTx); const result = getCreateItemResult(events); // tslint:disable-next-line:no-unused-expression expect(result.success).to.be.true; @@ -558,6 +606,9 @@ if (createMode === 'Fungible') { const createData = {fungible: {value: 10}}; tx = api.tx.nft.createItem(collectionId, owner, createData); + } else if (createMode === 'ReFungible') { + const createData = {refungible: {const_data: [], variable_data: [], pieces: 100}}; + tx = api.tx.nft.createItem(collectionId, owner, createData); } else { tx = api.tx.nft.createItem(collectionId, owner, createMode); } @@ -583,11 +634,25 @@ return newItemId; } -export async function enableWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number) { +export async function createItemExpectFailure( + sender: IKeyringPair, collectionId: number, createMode: string, owner: string = sender.address) { + await usingApi(async (api) => { + const tx = api.tx.nft.createItem(collectionId, owner, createMode); + const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected; + const result = getCreateItemResult(events); + + expect(result.success).to.be.false; + }); +} + +export async function setPublicAccessModeExpectSuccess( + sender: IKeyringPair, collectionId: number, + accessMode: 'Normal' | 'WhiteList', +) { await usingApi(async (api) => { // Run the transaction - const tx = api.tx.nft.setPublicAccessMode(collectionId, 'WhiteList'); + const tx = api.tx.nft.setPublicAccessMode(collectionId, accessMode); const events = await submitTransactionAsync(sender, tx); const result = getGenericResult(events); @@ -597,15 +662,23 @@ // What to expect // tslint:disable-next-line:no-unused-expression expect(result.success).to.be.true; - expect(collection.Access).to.be.equal('WhiteList'); + expect(collection.Access).to.be.equal(accessMode); }); } -export async function enablePublicMintingExpectSuccess(sender: IKeyringPair, collectionId: number) { +export async function enableWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number) { + await setPublicAccessModeExpectSuccess(sender, collectionId, 'WhiteList'); +} + +export async function disableWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number) { + await setPublicAccessModeExpectSuccess(sender, collectionId, 'Normal'); +} + +export async function setMintPermissionExpectSuccess(sender: IKeyringPair, collectionId: number, enabled: boolean) { await usingApi(async (api) => { // Run the transaction - const tx = api.tx.nft.setMintPermission(collectionId, true); + const tx = api.tx.nft.setMintPermission(collectionId, enabled); const events = await submitTransactionAsync(sender, tx); const result = getGenericResult(events); @@ -613,9 +686,33 @@ const collection: any = (await api.query.nft.collection(collectionId)).toJSON(); // What to expect + // tslint:disable-next-line:no-unused-expression expect(result.success).to.be.true; - expect(collection.MintMode).to.be.equal(true); + expect(collection.MintMode).to.be.equal(enabled); + }); +} + +export async function enablePublicMintingExpectSuccess(sender: IKeyringPair, collectionId: number) { + await setMintPermissionExpectSuccess(sender, collectionId, true); +} + +export async function setMintPermissionExpectFailure(sender: IKeyringPair, collectionId: number, enabled: boolean) { + await usingApi(async (api) => { + // Run the transaction + const tx = api.tx.nft.setMintPermission(collectionId, enabled); + const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected; + const result = getCreateCollectionResult(events); + // tslint:disable-next-line:no-unused-expression + expect(result.success).to.be.false; + }); +} + +export async function isWhitelisted(collectionId: number, address: string) { + let whitelisted: boolean = false; + await usingApi(async (api) => { + whitelisted = (await api.query.nft.whiteList(collectionId, address)).toJSON() as unknown as boolean; }); + return whitelisted; } export async function addToWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number, address: string) { @@ -640,6 +737,32 @@ }); } +export async function removeFromWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number, address: string) { + await usingApi(async (api) => { + // Run the transaction + const tx = api.tx.nft.removeFromWhiteList(collectionId, address); + const events = await submitTransactionAsync(sender, tx); + const result = getGenericResult(events); + + // What to expect + // tslint:disable-next-line:no-unused-expression + expect(result.success).to.be.true; + }); +} + +export async function removeFromWhiteListExpectFailure(sender: IKeyringPair, collectionId: number, address: string) { + await usingApi(async (api) => { + // Run the transaction + const tx = api.tx.nft.removeFromWhiteList(collectionId, address); + const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected; + const result = getGenericResult(events); + + // What to expect + // tslint:disable-next-line:no-unused-expression + expect(result.success).to.be.false; + }); +} + export const getDetailedCollectionInfo = async (api: ApiPromise, collectionId: number) : Promise => { return await api.query.nft.collection(collectionId) as unknown as ICollectionInterface;