difftreelog
Merge branch 'develop' into feature/contract-sponsoring-modes
in: master
34 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -382,9 +382,9 @@
[[package]]
name = "autocfg"
-version = "1.0.1"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "backtrace"
@@ -634,9 +634,9 @@
[[package]]
name = "block-buffer"
-version = "0.10.1"
+version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03588e54c62ae6d763e2a80090d50353b785795361b4ff5b3bf0a5097fc31c0b"
+checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
dependencies = [
"generic-array 0.14.5",
]
@@ -1097,9 +1097,9 @@
[[package]]
name = "core-foundation"
-version = "0.9.2"
+version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3"
+checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
dependencies = [
"core-foundation-sys",
"libc",
@@ -1229,9 +1229,9 @@
[[package]]
name = "crc32fast"
-version = "1.3.1"
+version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2209c310e29876f7f0b2721e7e26b84aff178aa3da5d091f9bfbf47669e60e3"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
"cfg-if 1.0.0",
]
@@ -1288,9 +1288,9 @@
[[package]]
name = "crypto-common"
-version = "0.1.1"
+version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "683d6b536309245c849479fba3da410962a43ed8e51c26b729208ec0ac2798d0"
+checksum = "a4600d695eb3f6ce1cd44e6e291adceb2cc3ab12f20a33777ecd0bf6eba34e06"
dependencies = [
"generic-array 0.14.5",
]
@@ -1898,13 +1898,12 @@
[[package]]
name = "digest"
-version = "0.10.1"
+version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b697d66081d42af4fba142d56918a3cb21dc8eb63372c6b85d14f44fb9c5979b"
+checksum = "8cb780dce4f9a8f5c087362b3a4595936b2019e7c8b30f2c3e9a7e94e6ae9837"
dependencies = [
- "block-buffer 0.10.1",
+ "block-buffer 0.10.2",
"crypto-common",
- "generic-array 0.14.5",
]
[[package]]
@@ -3241,9 +3240,9 @@
[[package]]
name = "httparse"
-version = "1.5.1"
+version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503"
+checksum = "9100414882e15fb7feccb4897e5f0ff0ff1ca7d1a86a23208ada4d7a18e6c6c4"
[[package]]
name = "httpdate"
@@ -4664,9 +4663,9 @@
[[package]]
name = "memmap2"
-version = "0.5.2"
+version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe3179b85e1fd8b14447cbebadb75e45a1002f541b925f0bfec366d56a81c56d"
+checksum = "057a3db23999c867821a7a59feb06a578fcb03685e983dff90daf9e7d24ac08f"
dependencies = [
"libc",
]
@@ -8805,7 +8804,7 @@
source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.16#4aeb95f7f38fcd519e2628f32f79044a8fef99d5"
dependencies = [
"impl-trait-for-tuples",
- "memmap2 0.5.2",
+ "memmap2 0.5.3",
"parity-scale-codec",
"sc-chain-spec-derive",
"sc-network",
@@ -9912,7 +9911,7 @@
dependencies = [
"cfg-if 1.0.0",
"cpufeatures 0.2.1",
- "digest 0.10.1",
+ "digest 0.10.2",
]
[[package]]
@@ -11644,7 +11643,7 @@
[[package]]
name = "unique-node"
-version = "0.9.14"
+version = "0.9.16"
dependencies = [
"cumulus-client-cli",
"cumulus-client-collator",
Dockerfile-parachaindiffbeforeafterboth--- a/Dockerfile-parachain
+++ b/Dockerfile-parachain
@@ -90,7 +90,7 @@
nvm install v15.5.0 && \
nvm use v15.5.0
-RUN git clone https://github.com/PureStake/polkadot-launch -b jlm-use-para-id-in-specs
+RUN git clone https://github.com/paritytech/polkadot-launch
RUN export NVM_DIR="$HOME/.nvm" && \
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && \
README.mddiffbeforeafterboth--- a/README.md
+++ b/README.md
@@ -29,26 +29,11 @@
Please see our [walk-thorugh instructions](doc/hackusama_walk_through.md) to try everything out!
-## Hackusama Update
-
-During the Kusama Hackaphon the following changes were made:
-
-- Enabled Smart Contracts Pallet
-- Enabled integration between Smart Contracts and Unique Pallet (required special edition of RC4 Substrate version)
-- Fixed misc. bugs in Unique Pallet
-- Deployed Unique TestNet. Public node available at wss://unique.usetech.com, custom UI types - see below in this README.
-- New Features:
- - Re-Fungible Token Mode
- - Off-Chain Schema to store token image URLs
- - Alternative economic model
- - Allow Lists and Public Mint Permission
-- Use example: [SubstraPunks Game](https://github.com/usetech-llc/substrapunks), fully hosted on IPFS and Unique Testnet
- Blockchain.
-
## Application Development
If you are building an application that operates NFT tokens, use [this document](doc/application_development.md).
+
## Building
Building Unique chain requires special versions of Rust and toolchain. We don't use the most recent versions of everything
@@ -57,8 +42,8 @@
1. Install Rust:
```bash
+sudo apt-get install git curl libssl-dev llvm pkg-config libclang-dev clang
curl https://sh.rustup.rs -sSf | sh
-sudo apt-get install libssl-dev pkg-config libclang-dev clang
```
2. Remove all installed toolchains with `rustup toolchain list` and `rustup toolchain uninstall <toolchain>`.
@@ -86,111 +71,52 @@
cargo build --release
```
-## Run
-
-You can start a development chain with:
-
-```bash
-cargo run -- --dev
-```
-
-Detailed logs may be shown by running the node with the following environment variables set:
-`RUST_LOG=debug RUST_BACKTRACE=1 cargo run -- --dev`.
+## Building as Parachain locally
-If you want to see the multi-node consensus algorithm in action locally, then you can create a local testnet with two
-validator nodes for Alice and Bob, who are the initial authorities of the genesis chain that have been endowed with
-testnet units. Give each node a name and expose them so they are listed on the Polkadot
-[telemetry site](https://telemetry.polkadot.io/#/Local%20Testnet). You'll need two terminal windows open.
+Note: checkout this project and all related projects (see below) in the sibling folders (both under the same folder)
-We'll start Alice's substrate node first on default TCP port 30333 with her chain database stored locally at
-`/tmp/alice`. The bootnode ID of her node is `QmQZ8TjTqeDj3ciwr93EJ95hxfDsb9pEYDizUAbWpigtQN`, which is generated from
-the `--node-key` value that we specify below:
+### Polkadot launch utility
-```bash
-cargo run -- \
- --base-path /tmp/alice \
- --chain=local \
- --alice \
- --node-key 0000000000000000000000000000000000000000000000000000000000000001 \
- --telemetry-url ws://telemetry.polkadot.io:1024 \
- --validator
```
-
-In the second terminal, we'll start Bob's substrate node on a different TCP port of 30334, and with his chain database
-stored locally at `/tmp/bob`. We'll specify a value for the `--bootnodes` option that will connect his node to Alice's
-bootnode ID on TCP port 30333:
-
-```bash
-cargo run -- \
- --base-path /tmp/bob \
- --bootnodes /ip4/127.0.0.1/tcp/30333/p2p/QmQZ8TjTqeDj3ciwr93EJ95hxfDsb9pEYDizUAbWpigtQN \
- --chain=local \
- --bob \
- --port 30334 \
- --telemetry-url ws://telemetry.polkadot.io:1024 \
- --validator
+git clone https://github.com/paritytech/polkadot-launch
```
-
-Additional CLI usage options are available and may be shown by running `cargo run -- --help`.
-
-## Building and Running as Parachain locally
-
-Rust toolchain: nightly-2021-11-11
-Note: checkout this project and polkadot project (see below) in the sibling folders (both under the same folder)
### Build relay
```
git clone https://github.com/paritytech/polkadot.git
cd polkadot
-git checkout release-v0.9.9
+git checkout release-v0.9.16
cargo build --release
```
-### Build parachain
+### Build Unique parachain
Run in the root of this project:
```
-cargo --build
+cargo build --release
```
-### Run 4-node Relay
-
-1. Download `rococo-custom-4.json` chain spec here: https://substrate.dev/cumulus-workshop/shared/chainspecs/rococo-custom-4.json
-2. Use these instructions to launch 4 nodes: https://substrate.dev/cumulus-workshop/#/en/2-relay-chain/1-launch
+### Build Acala parachain (optional, full config only)
-Example (Run in polkadot folder. Replace `12D3KooWNLAmKcyee3oqSgTMthaQVXaAcXeo8RrGCzMfMVA3B5on` with output from Alice node):
```
-./target/release/polkadot --alice --validator --base-path ./cumulus_relay0 --chain rococo-custom-4.json --port 50555 --ws-port 9944
-./target/release/polkadot --bob --validator --base-path ./cumulus_relay1 --chain rococo-custom-4.json --bootnodes /ip4/127.0.0.1/tcp/30333/p2p/12D3KooWNLAmKcyee3oqSgTMthaQVXaAcXeo8RrGCzMfMVA3B5on --port 50556 --ws-port 9945
-./target/release/polkadot --charlie --validator --base-path ./cumulus_relay1 --chain rococo-custom-4.json --bootnodes /ip4/127.0.0.1/tcp/30333/p2p/12D3KooWNLAmKcyee3oqSgTMthaQVXaAcXeo8RrGCzMfMVA3B5on --port 50557 --ws-port 9946
-./target/release/polkadot --dave --validator --base-path ./cumulus_relay1 --chain rococo-custom-4.json --bootnodes /ip4/127.0.0.1/tcp/30333/p2p/12D3KooWNLAmKcyee3oqSgTMthaQVXaAcXeo8RrGCzMfMVA3B5on --port 50558 --ws-port 9947
-
+git clone https://github.com/AcalaNetwork/Acala
+cd Acala
+git checkout 54db3acd409a0b787f116f20e163a3b24101ce38
+make build-release
```
-3. Export genesis state and runtime wasm from Unique parachain:
+## Running as Parachain locally
-Run from this project root:
```
-./target/release/unique-collator export-genesis-state --parachain-id 2000 > ./resources/para-2000-genesis
-./target/release/unique-collator export-genesis-wasm > ./resources/para-2000-wasm
+./launch-testnet.sh
```
-4. Run two parachain nodes:
-
-Replace `12D3KooWN1ah2bFQSysEFnwZqcmcVpDDR8UedXyo6xfzV1zDNMNg` with Alice or Bob relay ID
-
-Run from this project root:
+Optional, full setup with Acala and Statemint
```
-./target/release/unique-collator --alice --collator --force-authoring --base-path ./tmp/parachain-alice --parachain-id 2000 --port 40333 --ws-port 9844 -- --execution wasm --chain ../polkadot/rococo-custom-4.json --port 30343 --ws-port 9977
-./target/release/unique-collator --bob --collator --force-authoring --parachain-id 2000 --base-path ./tmp/parachain/bob --port 40334 --ws-port 9845 -- --execution wasm --chain ../polkadot/rococo-custom-4.json --port 30344 --ws-port 9978 --bootnodes /ip4/127.0.0.1/tcp/50556/p2p/12D3KooWN1ah2bFQSysEFnwZqcmcVpDDR8UedXyo6xfzV1zDNMNg
+./launch-testnet-full.sh
```
-
-4. Reserve parachain ID as described here: https://substrate.dev/cumulus-workshop/#/en/2-relay-chain/2-reserve
-5. Register parachain in relay as described here: https://substrate.dev/cumulus-workshop/#/en/3-parachains/2-register
-
-
## Run Integration Tests
1. Install all needed dependecies
@@ -202,40 +128,11 @@
2. Run tests
```
yarn test
-```
-
-## Benchmarks
-
-First of all, add rust toolchain and make it default.
-```bash
-rustup target add wasm32-unknown-unknown --toolchain nightly-2021-11-11
-```
-
-Then in "/node/src" run build command below
-```bash
-cargo +nightly-2021-11-11 build --release --features runtime-benchmarks
-```
-
-Run benchmark
-```bash
-target/release/unique-collator benchmark --chain dev --pallet "pallet_unique" --extrinsic "*" --repeat 1
```
-
-## UI custom types
-
-Moved to [runtime_types.json](./runtime_types.json).
-
-## Running Integration Tests
-See [tests/README.md](./tests/README.md).
## Code Formatting
-### Get formatter and linter settings into your branch (if you forked before they were introduced)
-```bash
-git cherry-pick -n 8ff77c21b0d30b2a4648fa35dbf61dfa9d3948a7
-```
-
### Apply formatting and clippy fixes
```bash
cargo clippy
@@ -252,84 +149,57 @@
cd tests && yarn eslint --ext .ts,.js src/
```
-## Re-Enabling Ink! Contracts
-Uncomment following lies:
-1. In node/rpc/Cargo.toml
-```
-# pallet-contracts-rpc = { version = "3.0", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.9' }
-```
+## Karura token transfer
-2. In node/rpc/src/lib.rs
-```
-// C::Api: pallet_contracts_rpc::ContractsRuntimeApi<Block, AccountId, Balance, BlockNumber, Hash>,
-...
-// use pallet_contracts_rpc::{Contracts, ContractsApi};
-...
-// io.extend_with(ContractsApi::to_delegate(Contracts::new(client.clone())));
+To get started, you need to open inbound and outbound hrmp channels.
+### Next, we need to register our asset at Karura.
```
+assetRegistry -> registerForeignAsset(location, metadata)
+location:
+ V0(X2(Parent, Parachain(PARA_ID)))
+metadata:
+ name OPL
+ symbol OPL
+ decimals 18
+minimalBalance 1
+```
-3. In runtime/Cargo.toml
+### Next, we can send tokens from Opal to Karura:
```
- # 'pallet-contracts/std',
- # 'pallet-contracts-primitives/std',
- # 'pallet-contracts-rpc-runtime-api/std',
- # 'pallet-contract-helpers/std',
-...
- # [dependencies.pallet-contracts]
- # git = 'https://github.com/paritytech/substrate.git'
- # default-features = false
- # branch = 'polkadot-v0.9.9'
- # version = '3.0.0'
+polkadotXcm -> reserveTransferAssets
+dest:
+ V0(X2(Parent, Parachain(<KARURA_PARA_ID>)))
+beneficiary:
+ X1(AccountId(Any, <ACCOUNT>))
+assets:
+ V1(Concrete(0,Here), Fungible(<AMOUNT>))
+feeAssetItem:
+ 0
+weightLimit:
+ <LIMIT>
+```
- # [dependencies.pallet-contracts-primitives]
- # git = 'https://github.com/paritytech/substrate.git'
- # default-features = false
- # branch = 'polkadot-v0.9.9'
- # version = '3.0.0'
+The result will be displayed in ChainState
+tokens -> accounts
- # [dependencies.pallet-contracts-rpc-runtime-api]
- # git = 'https://github.com/paritytech/substrate.git'
- # default-features = false
- # branch = 'polkadot-v0.9.9'
- # version = '3.0.0'
-...
- # pallet-contract-helpers = { path = '../pallets/contract-helpers', default-features = false, version = '0.1.0' }
+### To send tokens from Karura to Opal:
```
+xtokens -> transfer
-4. runtime/src/lib.rs
-```
-// use pallet_contracts::weights::WeightInfo;
-...
-// pub use pallet_timestamp::Call as TimestampCall;
-...
-// mod chain_extension;
-// use crate::chain_extension::{NFTExtension, Imbalance};
-...
-/*
-parameter_types! {
- pub TombstoneDeposit: Balance = deposit(
- ...
-}
-*/
-...
-//pallet_contract_helpers::ContractSponsorshipHandler<Runtime>,
-...
-// impl pallet_contract_helpers::Config for Runtime {}
-...
-// Contracts: pallet_contracts::{Pallet, Call, Storage, Event<T>},
-...
-// ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage},
-...
-//pallet_contract_helpers::ContractHelpersExtension<Runtime>,
-...
-/*
- impl pallet_contracts_rpc_runtime_api::ContractsApi<Block, AccountId, Balance, BlockNumber, Hash>
- for Runtime
- {
- ...
- }
-*/
+currencyId:
+ ForeingAsset
+ <TOKEN_ID>
-```
+amount:
+ <AMOUNT>
+dest:
+ V1
+ (
+ Parents:1,
+ X2(Parachain(<KARURA_PARA_ID>), AccountId(Any, <ACCOUNT>)
+ )
+destWeight:
+ <WEIGHT>
+```
\ No newline at end of file
client/rpc/src/lib.rsdiffbeforeafterboth--- a/client/rpc/src/lib.rs
+++ b/client/rpc/src/lib.rs
@@ -4,7 +4,7 @@
use jsonrpc_core::{Error as RpcError, ErrorCode, Result};
use jsonrpc_derive::rpc;
use up_data_structs::{Collection, CollectionId, CollectionStats, TokenId};
-use sp_api::{BlockId, BlockT, ProvideRuntimeApi};
+use sp_api::{BlockId, BlockT, ProvideRuntimeApi, ApiExt};
use sp_blockchain::HeaderBackend;
use up_rpc::UniqueApi as UniqueRuntimeApi;
@@ -132,7 +132,10 @@
}
macro_rules! pass_method {
- ($method_name:ident($($name:ident: $ty:ty),* $(,)?) -> $result:ty $(=> $mapper:expr)?) => {
+ (
+ $method_name:ident($($name:ident: $ty:ty),* $(,)?) -> $result:ty $(=> $mapper:expr)?
+ $(; changed_in $ver:expr, $changed_method_name:ident ($($changed_name:expr), * $(,)?) => $fixer:expr)*
+ ) => {
fn $method_name(
&self,
$(
@@ -142,8 +145,25 @@
) -> Result<$result> {
let api = self.client.runtime_api();
let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash));
+ let _api_version = if let Ok(Some(api_version)) =
+ api.api_version::<dyn UniqueRuntimeApi<Block, CrossAccountId, AccountId>>(&at)
+ {
+ api_version
+ } else {
+ // unreachable for our runtime
+ return Err(RpcError {
+ code: ErrorCode::InvalidParams,
+ message: "Api is not available".into(),
+ data: None,
+ })
+ };
- let result = api.$method_name(&at, $($name),*).map_err(|e| RpcError {
+ let result = $(if _api_version < $ver {
+ api.$changed_method_name(&at, $($changed_name),*).map(|r| r.map($fixer))
+ } else)*
+ { api.$method_name(&at, $($name),*) };
+
+ let result = result.map_err(|e| RpcError {
code: ErrorCode::ServerError(Error::RuntimeError.into()),
message: "Unable to query".into(),
data: Some(format!("{:?}", e).into()),
@@ -168,7 +188,10 @@
{
pass_method!(account_tokens(collection: CollectionId, account: CrossAccountId) -> Vec<TokenId>);
pass_method!(token_exists(collection: CollectionId, token: TokenId) -> bool);
- pass_method!(token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>);
+ pass_method!(
+ token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>;
+ changed_in 2, token_owner_before_version_2(collection, token) => |u| Some(u)
+ );
pass_method!(const_metadata(collection: CollectionId, token: TokenId) -> Vec<u8>);
pass_method!(variable_metadata(collection: CollectionId, token: TokenId) -> Vec<u8>);
pass_method!(collection_tokens(collection: CollectionId) -> u32);
doc/hackusama_update.mddiffbeforeafterboth--- /dev/null
+++ b/doc/hackusama_update.md
@@ -0,0 +1,15 @@
+## Hackusama Update
+
+During the Kusama Hackaphon the following changes were made:
+
+- Enabled Smart Contracts Pallet
+- Enabled integration between Smart Contracts and Unique Pallet (required special edition of RC4 Substrate version)
+- Fixed misc. bugs in Unique Pallet
+- Deployed Unique TestNet. Public node available at wss://unique.usetech.com, custom UI types - see below in this README.
+- New Features:
+ - Re-Fungible Token Mode
+ - Off-Chain Schema to store token image URLs
+ - Alternative economic model
+ - Allow Lists and Public Mint Permission
+- Use example: [SubstraPunks Game](https://github.com/usetech-llc/substrapunks), fully hosted on IPFS and Unique Testnet
+ Blockchain.
\ No newline at end of file
doc/re-enable_ink_contracts.mddiffbeforeafterboth--- /dev/null
+++ b/doc/re-enable_ink_contracts.md
@@ -0,0 +1,81 @@
+## Re-Enabling Ink! Contracts
+
+Uncomment following lies:
+1. In node/rpc/Cargo.toml
+```
+# pallet-contracts-rpc = { version = "3.0", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.9' }
+```
+
+2. In node/rpc/src/lib.rs
+```
+// C::Api: pallet_contracts_rpc::ContractsRuntimeApi<Block, AccountId, Balance, BlockNumber, Hash>,
+...
+// use pallet_contracts_rpc::{Contracts, ContractsApi};
+...
+// io.extend_with(ContractsApi::to_delegate(Contracts::new(client.clone())));
+
+```
+
+3. In runtime/Cargo.toml
+```
+ # 'pallet-contracts/std',
+ # 'pallet-contracts-primitives/std',
+ # 'pallet-contracts-rpc-runtime-api/std',
+ # 'pallet-contract-helpers/std',
+...
+ # [dependencies.pallet-contracts]
+ # git = 'https://github.com/paritytech/substrate.git'
+ # default-features = false
+ # branch = 'polkadot-v0.9.9'
+ # version = '3.0.0'
+
+ # [dependencies.pallet-contracts-primitives]
+ # git = 'https://github.com/paritytech/substrate.git'
+ # default-features = false
+ # branch = 'polkadot-v0.9.9'
+ # version = '3.0.0'
+
+ # [dependencies.pallet-contracts-rpc-runtime-api]
+ # git = 'https://github.com/paritytech/substrate.git'
+ # default-features = false
+ # branch = 'polkadot-v0.9.9'
+ # version = '3.0.0'
+...
+ # pallet-contract-helpers = { path = '../pallets/contract-helpers', default-features = false, version = '0.1.0' }
+```
+
+4. runtime/src/lib.rs
+```
+// use pallet_contracts::weights::WeightInfo;
+...
+// pub use pallet_timestamp::Call as TimestampCall;
+...
+// mod chain_extension;
+// use crate::chain_extension::{NFTExtension, Imbalance};
+...
+/*
+parameter_types! {
+ pub TombstoneDeposit: Balance = deposit(
+ ...
+}
+*/
+...
+//pallet_contract_helpers::ContractSponsorshipHandler<Runtime>,
+...
+// impl pallet_contract_helpers::Config for Runtime {}
+...
+// Contracts: pallet_contracts::{Pallet, Call, Storage, Event<T>},
+...
+// ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage},
+...
+//pallet_contract_helpers::ContractHelpersExtension<Runtime>,
+...
+/*
+ impl pallet_contracts_rpc_runtime_api::ContractsApi<Block, AccountId, Balance, BlockNumber, Hash>
+ for Runtime
+ {
+ ...
+ }
+*/
+
+```
\ No newline at end of file
launch-config-full.jsondiffbeforeafterboth--- /dev/null
+++ b/launch-config-full.json
@@ -0,0 +1,135 @@
+{
+ "relaychain": {
+ "bin": "../polkadot/target/release/polkadot",
+ "chain": "rococo-local",
+ "nodes": [
+ {
+ "name": "alice",
+ "wsPort": 9844,
+ "rpcPort": 9843,
+ "port": 30444,
+ "flags": [
+ "-lparachain::candidate_validation=debug",
+ "-lxcm=trace"
+ ]
+ },
+ {
+ "name": "bob",
+ "wsPort": 9855,
+ "rpcPort": 9854,
+ "port": 30555,
+ "flags": [
+ "-lparachain::candidate_validation=debug",
+ "-lxcm=trace"
+ ]
+ },
+ {
+ "name": "charlie",
+ "wsPort": 9866,
+ "rpcPort": 9865,
+ "port": 30666,
+ "flags": [
+ "-lparachain::candidate_validation=debug"
+ ]
+ },
+ {
+ "name": "dave",
+ "wsPort": 9877,
+ "rpcPort": 9876,
+ "port": 30777,
+ "flags": [
+ "-lparachain::candidate_validation=debug"
+ ]
+ },
+ {
+ "name": "eve",
+ "wsPort": 9888,
+ "rpcPort": 9887,
+ "port": 30888,
+ "flags": [
+ "-lparachain::candidate_validation=debug"
+ ]
+ }
+ ],
+ "genesis": {
+ "runtime": {
+ "runtime_genesis_config": {
+ "parachainsConfiguration": {
+ "config": {
+ "validation_upgrade_frequency": 1,
+ "validation_upgrade_delay": 1
+ }
+ }
+ }
+ }
+ }
+ },
+ "parachains": [
+ {
+ "bin": "../unique-chain/target/release/unique-collator",
+ "id": "1000",
+ "balance": "1000000000000000000000000",
+ "nodes": [
+ {
+ "port": 31200,
+ "wsPort": 9944,
+ "rpcPort": 9933,
+ "name": "alice",
+ "flags": [
+ "--rpc-cors=all",
+ "--unsafe-rpc-external",
+ "--unsafe-ws-external",
+ "-lxcm=trace"
+ ]
+ },
+ {
+ "port": 31201,
+ "wsPort": 9945,
+ "rpcPort": 9934,
+ "name": "bob",
+ "flags": [
+ "--rpc-cors=all",
+ "--unsafe-rpc-external",
+ "--unsafe-ws-external",
+ "-lxcm=trace"
+ ]
+ }
+ ]
+ },
+ {
+ "bin": "../Acala/target/release/acala",
+ "id": "2000",
+ "chain": "karura-dev",
+ "balance": "1000000000000000000000",
+ "nodes": [
+ {
+ "wsPort": 9946,
+ "port": 31202,
+ "name": "alice",
+ "flags": [
+ "--rpc-cors=all",
+ "--unsafe-rpc-external",
+ "--unsafe-ws-external",
+ "-lxcm=trace"
+ ]
+ }
+ ]
+ }
+ ],
+ "simpleParachains": [],
+ "hrmpChannels": [
+ {
+ "sender": 2000,
+ "recipient": 1000,
+ "maxCapacity": 8,
+ "maxMessageSize": 512
+ },
+ {
+ "sender": 1000,
+ "recipient": 2000,
+ "maxCapacity": 8,
+ "maxMessageSize": 512
+ }
+ ],
+ "finalization": false
+}
launch-config-westend.jsondiffbeforeafterboth--- a/launch-config-westend.json
+++ /dev/null
@@ -1,141 +0,0 @@
-{
- "relaychain": {
- "bin": "../polkadot/target/release/polkadot",
- "chain": "westend-local",
- "nodes": [
- {
- "name": "alice",
- "wsPort": 9844,
- "rpcPort": 9843,
- "port": 30444,
- "flags": [
- "-lparachain::candidate_validation=debug"
- ]
- },
- {
- "name": "bob",
- "wsPort": 9855,
- "rpcPort": 9854,
- "port": 30555,
- "flags": [
- "-lparachain::candidate_validation=debug"
- ]
- },
- {
- "name": "charlie",
- "wsPort": 9866,
- "rpcPort": 9865,
- "port": 30666,
- "flags": [
- "-lparachain::candidate_validation=debug"
- ]
- },
- {
- "name": "dave",
- "wsPort": 9877,
- "rpcPort": 9876,
- "port": 30777,
- "flags": [
- "-lparachain::candidate_validation=debug"
- ]
- },
- {
- "name": "eve",
- "wsPort": 9888,
- "rpcPort": 9886,
- "port": 30888,
- "flags": [
- "-lparachain::candidate_validation=debug"
- ]
- },
- {
- "name": "ferdie",
- "wsPort": 9897,
- "rpcPort": 9896,
- "port": 30999,
- "flags": [
- "-lparachain::candidate_validation=debug"
- ]
- }
- ],
- "genesis": {
- "runtime": {
- "runtime_genesis_config": {
- "parachainsConfiguration": {
- "config": {
- "validation_upgrade_frequency": 1,
- "validation_upgrade_delay": 1
- }
- }
- }
- }
- }
- },
- "parachains": [
- {
- "bin": "../unique-chain/target/release/unique-collator",
- "chain": "westend-local",
- "balance": "1000000000000000000000000",
- "nodes": [
- {
- "port": 31200,
- "wsPort": 9944,
- "rpcPort": 9933,
- "name": "alice",
- "flags": [
- "--rpc-cors=all",
- "--unsafe-rpc-external",
- "--unsafe-ws-external"
- ]
- },
- {
- "port": 31201,
- "wsPort": 9945,
- "rpcPort": 9934,
- "name": "bob",
- "flags": [
- "--rpc-cors=all",
- "--unsafe-rpc-external",
- "--unsafe-ws-external"
- ]
- },
- {
- "port": 31202,
- "wsPort": 9946,
- "rpcPort": 9935,
- "name": "charlie",
- "flags": [
- "--rpc-cors=all",
- "--unsafe-rpc-external",
- "--unsafe-ws-external"
- ]
- },
- {
- "port": 31203,
- "wsPort": 9947,
- "rpcPort": 9936,
- "name": "dave",
- "flags": [
- "--rpc-cors=all",
- "--unsafe-rpc-external",
- "--unsafe-ws-external"
- ]
- },
- {
- "port": 31204,
- "wsPort": 9948,
- "rpcPort": 9937,
- "name": "eve",
- "flags": [
- "--rpc-cors=all",
- "--unsafe-rpc-external",
- "--unsafe-ws-external"
- ]
- }
- ]
- }
- ],
- "simpleParachains": [],
- "hrmpChannels": [],
- "finalization": false
-}
launch-config.jsondiffbeforeafterboth--- a/launch-config.json
+++ b/launch-config.json
@@ -9,7 +9,8 @@
"rpcPort": 9843,
"port": 30444,
"flags": [
- "-lparachain::candidate_validation=debug"
+ "-lparachain::candidate_validation=debug",
+ "-lxcm=trace"
]
},
{
@@ -18,7 +19,8 @@
"rpcPort": 9854,
"port": 30555,
"flags": [
- "-lparachain::candidate_validation=debug"
+ "-lparachain::candidate_validation=debug",
+ "-lxcm=trace"
]
},
{
@@ -38,6 +40,15 @@
"flags": [
"-lparachain::candidate_validation=debug"
]
+ },
+ {
+ "name": "eve",
+ "wsPort": 9888,
+ "rpcPort": 9887,
+ "port": 30888,
+ "flags": [
+ "-lparachain::candidate_validation=debug"
+ ]
}
],
"genesis": {
@@ -67,7 +78,8 @@
"flags": [
"--rpc-cors=all",
"--unsafe-rpc-external",
- "--unsafe-ws-external"
+ "--unsafe-ws-external",
+ "-lxcm=trace"
]
},
{
@@ -78,7 +90,8 @@
"flags": [
"--rpc-cors=all",
"--unsafe-rpc-external",
- "--unsafe-ws-external"
+ "--unsafe-ws-external",
+ "-lxcm=trace"
]
}
]
launch-test-env.shdiffbeforeafterboth--- a/launch-test-env.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-cp launch-config.json ../polkadot-launch/launch-config.json
-
-cd ../polkadot-launch
-yarn install
-yarn build
-yarn start launch-config.json
launch-testnet-full.shdiffbeforeafterboth--- /dev/null
+++ b/launch-testnet-full.sh
@@ -0,0 +1,6 @@
+cp launch-config-full.json ../polkadot-launch/launch-config-full.json
+
+cd ../polkadot-launch
+yarn install
+yarn build
+yarn start launch-config-full.json
launch-testnet.shdiffbeforeafterboth--- /dev/null
+++ b/launch-testnet.sh
@@ -0,0 +1,6 @@
+cp launch-config.json ../polkadot-launch/launch-config.json
+
+cd ../polkadot-launch
+yarn install
+yarn build
+yarn start launch-config.json
node/cli/Cargo.tomldiffbeforeafterboth--- a/node/cli/Cargo.toml
+++ b/node/cli/Cargo.toml
@@ -257,7 +257,7 @@
license = 'All Rights Reserved'
name = 'unique-node'
repository = 'https://github.com/UniqueNetwork/unique-chain'
-version = '0.9.14'
+version = '0.9.16'
[[bin]]
name = 'unique-collator'
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -14,7 +14,10 @@
use up_data_structs::{
COLLECTION_NUMBER_LIMIT, Collection, CollectionId, CreateItemData, ExistenceRequirement,
MAX_TOKEN_PREFIX_LENGTH, COLLECTION_ADMINS_LIMIT, MetaUpdatePermission, Pays, PostDispatchInfo,
- TokenId, Weight, WithdrawReasons, CollectionStats, CustomDataLimit,
+ TokenId, Weight, WithdrawReasons, CollectionStats, MAX_TOKEN_OWNERSHIP, CollectionMode,
+ NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, MAX_SPONSOR_TIMEOUT, CUSTOM_DATA_LIMIT, CollectionLimits,
+ CustomDataLimit, CreateCollectionData, SponsorshipState,
};
pub use pallet::*;
use sp_core::H160;
@@ -285,6 +288,10 @@
TokenVariableDataLimitExceeded,
/// Exceeded max admin count
CollectionAdminCountExceeded,
+ /// Collection limit bounds per collection exceeded
+ CollectionLimitBoundsExceeded,
+ /// Tried to enable permissions which are only permitted to be disabled
+ OwnerPermissionsCantBeReverted,
/// Collection settings not allowing items transferring
TransferNotAllowed,
@@ -300,7 +307,7 @@
/// Item balance not enough.
TokenValueTooLow,
/// Requested value more than approved.
- TokenValueNotEnough,
+ ApprovedValueTooLow,
/// Tried to approve more than owned
CantApproveMoreThanOwned,
@@ -395,7 +402,10 @@
}
impl<T: Config> Pallet<T> {
- pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {
+ pub fn init_collection(
+ owner: T::AccountId,
+ data: CreateCollectionData<T::AccountId>,
+ ) -> Result<CollectionId, DispatchError> {
{
ensure!(
data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,
@@ -418,6 +428,29 @@
// =========
+ let collection = Collection {
+ owner: owner.clone(),
+ name: data.name,
+ mode: data.mode.clone(),
+ mint_mode: false,
+ access: data.access.unwrap_or_default(),
+ description: data.description,
+ token_prefix: data.token_prefix,
+ offchain_schema: data.offchain_schema,
+ schema_version: data.schema_version.unwrap_or_default(),
+ sponsorship: data
+ .pending_sponsor
+ .map(SponsorshipState::Unconfirmed)
+ .unwrap_or_default(),
+ variable_on_chain_schema: data.variable_on_chain_schema,
+ const_on_chain_schema: data.const_on_chain_schema,
+ limits: data
+ .limits
+ .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))
+ .unwrap_or_else(|| Ok(CollectionLimits::default()))?,
+ meta_update_permission: data.meta_update_permission.unwrap_or_default(),
+ };
+
// Take a (non-refundable) deposit of collection creation
{
let mut imbalance =
@@ -429,7 +462,7 @@
),
);
<T as Config>::Currency::settle(
- &data.owner,
+ &owner,
imbalance,
WithdrawReasons::TRANSFER,
ExistenceRequirement::KeepAlive,
@@ -438,12 +471,8 @@
}
<CreatedCollectionCount<T>>::put(created_count);
- <Pallet<T>>::deposit_event(Event::CollectionCreated(
- id,
- data.mode.id(),
- data.owner.clone(),
- ));
- <CollectionById<T>>::insert(id, data);
+ <Pallet<T>>::deposit_event(Event::CollectionCreated(id, data.mode.id(), owner.clone()));
+ <CollectionById<T>>::insert(id, collection);
Ok(id)
}
@@ -527,6 +556,61 @@
Ok(())
}
+
+ pub fn clamp_limits(
+ mode: CollectionMode,
+ old_limit: &CollectionLimits,
+ mut new_limit: CollectionLimits,
+ ) -> Result<CollectionLimits, DispatchError> {
+ macro_rules! limit_default {
+ ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{
+ $(
+ if let Some($new) = $new.$field {
+ let $old = $old.$field($($arg)?);
+ let _ = $new;
+ let _ = $old;
+ $check
+ } else {
+ $new.$field = $old.$field
+ }
+ )*
+ }};
+ }
+
+ limit_default!(old_limit, new_limit,
+ account_token_ownership_limit => ensure!(
+ new_limit <= MAX_TOKEN_OWNERSHIP,
+ <Error<T>>::CollectionLimitBoundsExceeded,
+ ),
+ sponsor_transfer_timeout(match mode {
+ CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,
+ CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ }) => ensure!(
+ new_limit <= MAX_SPONSOR_TIMEOUT,
+ <Error<T>>::CollectionLimitBoundsExceeded,
+ ),
+ sponsored_data_size => ensure!(
+ new_limit <= CUSTOM_DATA_LIMIT,
+ <Error<T>>::CollectionLimitBoundsExceeded,
+ ),
+ token_limit => ensure!(
+ old_limit >= new_limit && new_limit > 0,
+ <Error<T>>::CollectionTokenLimitExceeded
+ ),
+ owner_can_transfer => ensure!(
+ old_limit || !new_limit,
+ <Error<T>>::OwnerPermissionsCantBeReverted,
+ ),
+ owner_can_destroy => ensure!(
+ old_limit || !new_limit,
+ <Error<T>>::OwnerPermissionsCantBeReverted,
+ ),
+ sponsored_data_rate_limit => {},
+ transfers_enabled => {},
+ );
+ Ok(new_limit)
+ }
}
#[macro_export]
pallets/fungible/src/lib.rsdiffbeforeafterboth--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -2,7 +2,7 @@
use core::ops::Deref;
use frame_support::{ensure};
-use up_data_structs::{AccessMode, Collection, CollectionId, TokenId};
+use up_data_structs::{AccessMode, CollectionId, TokenId, CreateCollectionData};
use pallet_common::{
Error as CommonError, Event as CommonEvent, Pallet as PalletCommon, account::CrossAccountId,
};
@@ -100,8 +100,11 @@
}
impl<T: Config> Pallet<T> {
- pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {
- <PalletCommon<T>>::init_collection(data)
+ pub fn init_collection(
+ owner: T::AccountId,
+ data: CreateCollectionData<T::AccountId>,
+ ) -> Result<CollectionId, DispatchError> {
+ <PalletCommon<T>>::init_collection(owner, data)
}
pub fn destroy_collection(
collection: FungibleHandle<T>,
@@ -343,7 +346,7 @@
if allowance.is_none() {
ensure!(
collection.ignores_allowance(spender),
- <CommonError<T>>::TokenValueNotEnough
+ <CommonError<T>>::ApprovedValueTooLow
);
}
@@ -374,7 +377,7 @@
if allowance.is_none() {
ensure!(
collection.ignores_allowance(spender),
- <CommonError<T>>::TokenValueNotEnough
+ <CommonError<T>>::ApprovedValueTooLow
);
}
pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -2,7 +2,7 @@
use erc::ERC721Events;
use frame_support::{BoundedVec, ensure};
-use up_data_structs::{AccessMode, Collection, CollectionId, CustomDataLimit, TokenId};
+use up_data_structs::{AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData};
use pallet_common::{
Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, account::CrossAccountId,
};
@@ -140,8 +140,11 @@
// unchecked calls skips any permission checks
impl<T: Config> Pallet<T> {
- pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {
- <PalletCommon<T>>::init_collection(data)
+ pub fn init_collection(
+ owner: T::AccountId,
+ data: CreateCollectionData<T::AccountId>,
+ ) -> Result<CollectionId, DispatchError> {
+ <PalletCommon<T>>::init_collection(owner, data)
}
pub fn destroy_collection(
collection: NonfungibleHandle<T>,
@@ -492,7 +495,7 @@
if <Allowance<T>>::get((collection.id, token)).as_ref() != Some(spender) {
ensure!(
collection.ignores_allowance(spender),
- <CommonError<T>>::TokenValueNotEnough
+ <CommonError<T>>::ApprovedValueTooLow
);
}
@@ -520,7 +523,7 @@
if <Allowance<T>>::get((collection.id, token)).as_ref() != Some(spender) {
ensure!(
collection.ignores_allowance(spender),
- <CommonError<T>>::TokenValueNotEnough
+ <CommonError<T>>::ApprovedValueTooLow
);
}
pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -2,7 +2,7 @@
use frame_support::{ensure, BoundedVec};
use up_data_structs::{
- AccessMode, Collection, CollectionId, CustomDataLimit, MAX_REFUNGIBLE_PIECES, TokenId,
+ AccessMode, CollectionId, CustomDataLimit, MAX_REFUNGIBLE_PIECES, TokenId, CreateCollectionData,
};
use pallet_common::{
Error as CommonError, Event as CommonEvent, Pallet as PalletCommon, account::CrossAccountId,
@@ -155,8 +155,11 @@
// unchecked calls skips any permission checks
impl<T: Config> Pallet<T> {
- pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {
- <PalletCommon<T>>::init_collection(data)
+ pub fn init_collection(
+ owner: T::AccountId,
+ data: CreateCollectionData<T::AccountId>,
+ ) -> Result<CollectionId, DispatchError> {
+ <PalletCommon<T>>::init_collection(owner, data)
}
pub fn destroy_collection(
collection: RefungibleHandle<T>,
@@ -528,7 +531,7 @@
if allowance.is_none() {
ensure!(
collection.ignores_allowance(spender),
- <CommonError<T>>::TokenValueNotEnough
+ <CommonError<T>>::ApprovedValueTooLow
);
}
@@ -561,7 +564,7 @@
if allowance.is_none() {
ensure!(
collection.ignores_allowance(spender),
- <CommonError<T>>::TokenValueNotEnough
+ <CommonError<T>>::ApprovedValueTooLow
);
}
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -36,13 +36,11 @@
use frame_system::{self as system, ensure_signed};
use sp_runtime::{sp_std::prelude::Vec};
use up_data_structs::{
- MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, CUSTOM_DATA_LIMIT,
- VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, OFFCHAIN_SCHEMA_LIMIT,
- FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
- NFT_SPONSOR_TRANSFER_TIMEOUT, MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH,
- MAX_TOKEN_PREFIX_LENGTH, AccessMode, Collection, CreateItemData, CollectionLimits,
- CollectionId, CollectionMode, TokenId, SchemaVersion, SponsorshipState, MetaUpdatePermission,
- CustomDataLimit,
+ MAX_DECIMAL_POINTS, VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT,
+ OFFCHAIN_SCHEMA_LIMIT, MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH,
+ MAX_TOKEN_PREFIX_LENGTH, AccessMode, CreateItemData, CollectionLimits, CollectionId,
+ CollectionMode, TokenId, SchemaVersion, SponsorshipState, MetaUpdatePermission,
+ CreateCollectionData, CustomDataLimit,
};
use pallet_common::{
account::CrossAccountId, CollectionHandle, Pallet as PalletCommon, Error as CommonError,
@@ -84,10 +82,6 @@
ConfirmUnsetSponsorFail,
/// Length of items properties must be greater than 0.
EmptyArgument,
- /// Collection limit bounds per collection exceeded
- CollectionLimitBoundsExceeded,
- /// Tried to enable permissions which are only permitted to be disabled
- OwnerPermissionsCantBeReverted,
}
}
@@ -321,42 +315,39 @@
// returns collection ID
#[weight = <SelfWeightOf<T>>::create_collection()]
#[transactional]
+ #[deprecated]
pub fn create_collection(origin,
collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,
collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,
token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,
- mode: CollectionMode) -> DispatchResult {
-
- // Anyone can create a collection
- let who = ensure_signed(origin)?;
-
- // Create new collection
- let new_collection = Collection {
- owner: who,
+ mode: CollectionMode) -> DispatchResult {
+ let data: CreateCollectionData<T::AccountId> = CreateCollectionData {
name: collection_name,
- mode: mode.clone(),
- mint_mode: false,
- access: AccessMode::Normal,
description: collection_description,
token_prefix,
- offchain_schema: BoundedVec::default(),
- schema_version: SchemaVersion::ImageURL,
- sponsorship: SponsorshipState::Disabled,
- variable_on_chain_schema: BoundedVec::default(),
- const_on_chain_schema: BoundedVec::default(),
- limits: Default::default(),
- meta_update_permission: Default::default(),
+ mode,
+ ..Default::default()
};
+ Self::create_collection_ex(origin, data)
+ }
- let _id = match mode {
- CollectionMode::NFT => {<PalletNonfungible<T>>::init_collection(new_collection)?},
+ /// This method creates a collection
+ ///
+ /// Prefer it to deprecated [`created_collection`] method
+ #[weight = <SelfWeightOf<T>>::create_collection()]
+ #[transactional]
+ pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {
+ let owner = ensure_signed(origin)?;
+
+ let _id = match data.mode {
+ CollectionMode::NFT => {<PalletNonfungible<T>>::init_collection(owner, data)?},
CollectionMode::Fungible(decimal_points) => {
// check params
ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);
- <PalletFungible<T>>::init_collection(new_collection)?
+ <PalletFungible<T>>::init_collection(owner, data)?
}
CollectionMode::ReFungible => {
- <PalletRefungible<T>>::init_collection(new_collection)?
+ <PalletRefungible<T>>::init_collection(owner, data)?
}
};
@@ -1093,61 +1084,12 @@
collection_id: CollectionId,
new_limit: CollectionLimits,
) -> DispatchResult {
- let mut new_limit = new_limit;
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;
target_collection.check_is_owner(&sender)?;
let old_limit = &target_collection.limits;
- macro_rules! limit_default {
- ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{
- $(
- if let Some($new) = $new.$field {
- let $old = $old.$field($($arg)?);
- let _ = $new;
- let _ = $old;
- $check
- } else {
- $new.$field = $old.$field
- }
- )*
- }};
- }
-
- limit_default!(old_limit, new_limit,
- account_token_ownership_limit => ensure!(
- new_limit <= MAX_TOKEN_OWNERSHIP,
- <Error<T>>::CollectionLimitBoundsExceeded,
- ),
- sponsor_transfer_timeout(match target_collection.mode {
- CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,
- CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
- CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
- }) => ensure!(
- new_limit <= MAX_SPONSOR_TIMEOUT,
- <Error<T>>::CollectionLimitBoundsExceeded,
- ),
- sponsored_data_size => ensure!(
- new_limit <= CUSTOM_DATA_LIMIT,
- <Error<T>>::CollectionLimitBoundsExceeded,
- ),
- token_limit => ensure!(
- old_limit >= new_limit && new_limit > 0,
- <CommonError<T>>::CollectionTokenLimitExceeded
- ),
- owner_can_transfer => ensure!(
- old_limit || !new_limit,
- <Error<T>>::OwnerPermissionsCantBeReverted,
- ),
- owner_can_destroy => ensure!(
- old_limit || !new_limit,
- <Error<T>>::OwnerPermissionsCantBeReverted,
- ),
- sponsored_data_rate_limit => {},
- transfers_enabled => {},
- );
-
- target_collection.limits = new_limit;
+ target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;
<Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(
collection_id
pallets/unique/src/tests.rsdiffbeforeafterboth--- a/pallets/unique/src/tests.rs
+++ b/pallets/unique/src/tests.rs
@@ -5,7 +5,7 @@
use up_data_structs::{
COLLECTION_NUMBER_LIMIT, CollectionId, CreateItemData, CreateFungibleData, CreateNftData,
CreateReFungibleData, MAX_DECIMAL_POINTS, COLLECTION_ADMINS_LIMIT, MetaUpdatePermission,
- TokenId,
+ TokenId, MAX_TOKEN_OWNERSHIP,
};
use frame_support::{assert_noop, assert_ok};
use sp_std::convert::TryInto;
@@ -601,7 +601,7 @@
1
)
.map_err(|e| e.error),
- CommonError::<Test>::TokenValueNotEnough
+ CommonError::<Test>::ApprovedValueTooLow
);
// do approve
@@ -916,7 +916,7 @@
4
)
.map_err(|e| e.error),
- CommonError::<Test>::TokenValueNotEnough
+ CommonError::<Test>::ApprovedValueTooLow
);
});
}
primitives/data-structs/src/lib.rsdiffbeforeafterboth1#![cfg_attr(not(feature = "std"), no_std)]23use core::convert::{TryFrom, TryInto};45#[cfg(feature = "serde")]6pub use serde::{Serialize, Deserialize};78use sp_core::U256;9use sp_runtime::{ArithmeticError, sp_std::prelude::Vec};10use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};11pub use frame_support::{12 BoundedVec, construct_runtime, decl_event, decl_module, decl_storage, decl_error,13 dispatch::DispatchResult,14 ensure, fail, parameter_types,15 traits::{16 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,17 Randomness, IsSubType, WithdrawReasons,18 },19 weights::{20 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},21 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,22 WeightToFeePolynomial, DispatchClass,23 },24 StorageValue, transactional,25 pallet_prelude::ConstU32,26};27use derivative::Derivative;28use scale_info::TypeInfo;2930pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;31pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;32pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;3334pub const MAX_TOKEN_OWNERSHIP: u32 = if cfg!(not(feature = "limit-testing")) {35 100_00036} else {37 1038};39pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {40 100_00041} else {42 1043};44pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {45 204846} else {47 1048};49pub const COLLECTION_ADMINS_LIMIT: u32 = 5;50pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX;51pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {52 1_000_00053} else {54 1055};5657// Timeouts for item types in passed blocks58pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;59pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;60pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;6162pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5;6364// Schema limits65pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 8192;66pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 8192;67pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 1048576;6869pub const MAX_COLLECTION_NAME_LENGTH: u32 = 64;70pub const MAX_COLLECTION_DESCRIPTION_LENGTH: u32 = 256;71pub const MAX_TOKEN_PREFIX_LENGTH: u32 = 16;7273/// How much items can be created per single74/// create_many call75pub const MAX_ITEMS_PER_BATCH: u32 = 200;7677parameter_types! {78 pub const CustomDataLimit: u32 = CUSTOM_DATA_LIMIT;79}8081#[derive(82 Encode,83 Decode,84 PartialEq,85 Eq,86 PartialOrd,87 Ord,88 Clone,89 Copy,90 Debug,91 Default,92 TypeInfo,93 MaxEncodedLen,94)]95#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]96pub struct CollectionId(pub u32);97impl EncodeLike<u32> for CollectionId {}98impl EncodeLike<CollectionId> for u32 {}99100#[derive(101 Encode,102 Decode,103 PartialEq,104 Eq,105 PartialOrd,106 Ord,107 Clone,108 Copy,109 Debug,110 Default,111 TypeInfo,112 MaxEncodedLen,113)]114#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]115pub struct TokenId(pub u32);116impl EncodeLike<u32> for TokenId {}117impl EncodeLike<TokenId> for u32 {}118119impl TokenId {120 pub fn try_next(self) -> Result<TokenId, ArithmeticError> {121 self.0122 .checked_add(1)123 .ok_or(ArithmeticError::Overflow)124 .map(Self)125 }126}127128impl From<TokenId> for U256 {129 fn from(t: TokenId) -> Self {130 t.0.into()131 }132}133134impl TryFrom<U256> for TokenId {135 type Error = &'static str;136137 fn try_from(value: U256) -> Result<Self, Self::Error> {138 Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))139 }140}141142pub struct OverflowError;143impl From<OverflowError> for &'static str {144 fn from(_: OverflowError) -> Self {145 "overflow occured"146 }147}148149pub type DecimalPoints = u8;150151#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]152#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]153pub enum CollectionMode {154 NFT,155 // decimal points156 Fungible(DecimalPoints),157 ReFungible,158}159160impl CollectionMode {161 pub fn id(&self) -> u8 {162 match self {163 CollectionMode::NFT => 1,164 CollectionMode::Fungible(_) => 2,165 CollectionMode::ReFungible => 3,166 }167 }168}169170pub trait SponsoringResolve<AccountId, Call> {171 fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;172}173174#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]175#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]176pub enum AccessMode {177 Normal,178 AllowList,179}180impl Default for AccessMode {181 fn default() -> Self {182 Self::Normal183 }184}185186#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]187#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]188pub enum SchemaVersion {189 ImageURL,190 Unique,191}192impl Default for SchemaVersion {193 fn default() -> Self {194 Self::ImageURL195 }196}197198#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]199#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]200pub struct Ownership<AccountId> {201 pub owner: AccountId,202 pub fraction: u128,203}204205#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]206#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]207pub enum SponsorshipState<AccountId> {208 /// The fees are applied to the transaction sender209 Disabled,210 Unconfirmed(AccountId),211 /// Transactions are sponsored by specified account212 Confirmed(AccountId),213}214215impl<AccountId> SponsorshipState<AccountId> {216 pub fn sponsor(&self) -> Option<&AccountId> {217 match self {218 Self::Confirmed(sponsor) => Some(sponsor),219 _ => None,220 }221 }222223 pub fn pending_sponsor(&self) -> Option<&AccountId> {224 match self {225 Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),226 _ => None,227 }228 }229230 pub fn confirmed(&self) -> bool {231 matches!(self, Self::Confirmed(_))232 }233}234235impl<T> Default for SponsorshipState<T> {236 fn default() -> Self {237 Self::Disabled238 }239}240241#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]242#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]243pub struct Collection<AccountId> {244 pub owner: AccountId,245 pub mode: CollectionMode,246 pub access: AccessMode,247 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]248 pub name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,249 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]250 pub description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,251 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]252 pub token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,253 pub mint_mode: bool,254 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]255 pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,256 pub schema_version: SchemaVersion,257 pub sponsorship: SponsorshipState<AccountId>,258 pub limits: CollectionLimits, // Collection private restrictions259 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]260 pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,261 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]262 pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,263 pub meta_update_permission: MetaUpdatePermission,264}265266#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]267#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]268pub struct NftItemType<AccountId> {269 pub owner: AccountId,270 pub const_data: Vec<u8>,271 pub variable_data: Vec<u8>,272}273274#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]275#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]276pub struct FungibleItemType {277 pub value: u128,278}279280#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]281#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]282pub struct ReFungibleItemType<AccountId> {283 pub owner: Vec<Ownership<AccountId>>,284 pub const_data: Vec<u8>,285 pub variable_data: Vec<u8>,286}287288/// All fields are wrapped in `Option`s, where None means chain default289#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]290#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]291pub struct CollectionLimits {292 pub account_token_ownership_limit: Option<u32>,293 pub sponsored_data_size: Option<u32>,294 /// None - setVariableMetadata is not sponsored295 /// Some(v) - setVariableMetadata is sponsored296 /// if there is v block between txs297 pub sponsored_data_rate_limit: Option<(Option<u32>,)>,298 pub token_limit: Option<u32>,299300 // Timeouts for item types in passed blocks301 pub sponsor_transfer_timeout: Option<u32>,302 pub sponsor_approve_timeout: Option<u32>,303 pub owner_can_transfer: Option<bool>,304 pub owner_can_destroy: Option<bool>,305 pub transfers_enabled: Option<bool>,306}307308impl CollectionLimits {309 pub fn account_token_ownership_limit(&self) -> u32 {310 self.account_token_ownership_limit311 .unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)312 .min(MAX_TOKEN_OWNERSHIP)313 }314 pub fn sponsored_data_size(&self) -> u32 {315 self.sponsored_data_size316 .unwrap_or(CUSTOM_DATA_LIMIT)317 .min(CUSTOM_DATA_LIMIT)318 }319 pub fn token_limit(&self) -> u32 {320 self.token_limit321 .unwrap_or(COLLECTION_TOKEN_LIMIT)322 .min(COLLECTION_TOKEN_LIMIT)323 }324 pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {325 self.sponsor_transfer_timeout326 .unwrap_or(default)327 .min(MAX_SPONSOR_TIMEOUT)328 }329 pub fn sponsor_approve_timeout(&self) -> u32 {330 self.sponsor_approve_timeout331 .unwrap_or(SPONSOR_APPROVE_TIMEOUT)332 .min(MAX_SPONSOR_TIMEOUT)333 }334 pub fn owner_can_transfer(&self) -> bool {335 self.owner_can_transfer.unwrap_or(true)336 }337 pub fn owner_can_destroy(&self) -> bool {338 self.owner_can_destroy.unwrap_or(true)339 }340 pub fn transfers_enabled(&self) -> bool {341 self.transfers_enabled.unwrap_or(true)342 }343 pub fn sponsored_data_rate_limit(&self) -> Option<u32> {344 self.sponsored_data_rate_limit345 .unwrap_or((None,))346 .0347 .map(|v| v.min(MAX_SPONSOR_TIMEOUT))348 }349}350351/// BoundedVec doesn't supports serde352#[cfg(feature = "serde1")]353mod bounded_serde {354 use core::convert::TryFrom;355 use frame_support::{BoundedVec, traits::Get};356 use serde::{357 ser::{self, Serialize},358 de::{self, Deserialize, Error},359 };360 use sp_std::vec::Vec;361362 pub fn serialize<D, V, S>(value: &BoundedVec<V, S>, serializer: D) -> Result<D::Ok, D::Error>363 where364 D: ser::Serializer,365 V: Serialize,366 {367 (value as &Vec<_>).serialize(serializer)368 }369370 pub fn deserialize<'de, D, V, S>(deserializer: D) -> Result<BoundedVec<V, S>, D::Error>371 where372 D: de::Deserializer<'de>,373 V: de::Deserialize<'de>,374 S: Get<u32>,375 {376 // TODO: Implement custom visitor, which will limit vec size at parse time? Will serde only be used by chainspec?377 let vec = <Vec<V>>::deserialize(deserializer)?;378 let len = vec.len();379 TryFrom::try_from(vec).map_err(|_| D::Error::invalid_length(len, &"lesser size"))380 }381}382383#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]384#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]385#[derivative(Debug)]386pub struct CreateNftData {387 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]388 #[derivative(Debug = "ignore")]389 pub const_data: BoundedVec<u8, CustomDataLimit>,390 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]391 #[derivative(Debug = "ignore")]392 pub variable_data: BoundedVec<u8, CustomDataLimit>,393}394395#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]396#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]397pub struct CreateFungibleData {398 pub value: u128,399}400401#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]402#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]403#[derivative(Debug)]404pub struct CreateReFungibleData {405 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]406 #[derivative(Debug = "ignore")]407 pub const_data: BoundedVec<u8, CustomDataLimit>,408 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]409 #[derivative(Debug = "ignore")]410 pub variable_data: BoundedVec<u8, CustomDataLimit>,411 pub pieces: u128,412}413414#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]415#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]416pub enum MetaUpdatePermission {417 ItemOwner,418 Admin,419 None,420}421422impl Default for MetaUpdatePermission {423 fn default() -> Self {424 Self::ItemOwner425 }426}427428#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]429#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]430pub enum CreateItemData {431 NFT(CreateNftData),432 Fungible(CreateFungibleData),433 ReFungible(CreateReFungibleData),434}435436impl CreateItemData {437 pub fn data_size(&self) -> usize {438 match self {439 CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),440 CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),441 _ => 0,442 }443 }444}445446impl From<CreateNftData> for CreateItemData {447 fn from(item: CreateNftData) -> Self {448 CreateItemData::NFT(item)449 }450}451452impl From<CreateReFungibleData> for CreateItemData {453 fn from(item: CreateReFungibleData) -> Self {454 CreateItemData::ReFungible(item)455 }456}457458impl From<CreateFungibleData> for CreateItemData {459 fn from(item: CreateFungibleData) -> Self {460 CreateItemData::Fungible(item)461 }462}463464#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]465#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]466pub struct CollectionStats {467 pub created: u32,468 pub destroyed: u32,469 pub alive: u32,470}1#![cfg_attr(not(feature = "std"), no_std)]23use core::convert::{TryFrom, TryInto};45#[cfg(feature = "serde")]6pub use serde::{Serialize, Deserialize};78use sp_core::U256;9use sp_runtime::{ArithmeticError, sp_std::prelude::Vec};10use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};11pub use frame_support::{12 BoundedVec, construct_runtime, decl_event, decl_module, decl_storage, decl_error,13 dispatch::DispatchResult,14 ensure, fail, parameter_types,15 traits::{16 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,17 Randomness, IsSubType, WithdrawReasons,18 },19 weights::{20 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},21 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,22 WeightToFeePolynomial, DispatchClass,23 },24 StorageValue, transactional,25 pallet_prelude::ConstU32,26};27use derivative::Derivative;28use scale_info::TypeInfo;2930pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;31pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;32pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;3334pub const MAX_TOKEN_OWNERSHIP: u32 = if cfg!(not(feature = "limit-testing")) {35 100_00036} else {37 1038};39pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {40 100_00041} else {42 1043};44pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {45 204846} else {47 1048};49pub const COLLECTION_ADMINS_LIMIT: u32 = 5;50pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX;51pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {52 1_000_00053} else {54 1055};5657// Timeouts for item types in passed blocks58pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;59pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;60pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;6162pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5;6364// Schema limits65pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 8192;66pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 8192;67pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 1048576;6869pub const MAX_COLLECTION_NAME_LENGTH: u32 = 64;70pub const MAX_COLLECTION_DESCRIPTION_LENGTH: u32 = 256;71pub const MAX_TOKEN_PREFIX_LENGTH: u32 = 16;7273/// How much items can be created per single74/// create_many call75pub const MAX_ITEMS_PER_BATCH: u32 = 200;7677parameter_types! {78 pub const CustomDataLimit: u32 = CUSTOM_DATA_LIMIT;79}8081#[derive(82 Encode,83 Decode,84 PartialEq,85 Eq,86 PartialOrd,87 Ord,88 Clone,89 Copy,90 Debug,91 Default,92 TypeInfo,93 MaxEncodedLen,94)]95#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]96pub struct CollectionId(pub u32);97impl EncodeLike<u32> for CollectionId {}98impl EncodeLike<CollectionId> for u32 {}99100#[derive(101 Encode,102 Decode,103 PartialEq,104 Eq,105 PartialOrd,106 Ord,107 Clone,108 Copy,109 Debug,110 Default,111 TypeInfo,112 MaxEncodedLen,113)]114#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]115pub struct TokenId(pub u32);116impl EncodeLike<u32> for TokenId {}117impl EncodeLike<TokenId> for u32 {}118119impl TokenId {120 pub fn try_next(self) -> Result<TokenId, ArithmeticError> {121 self.0122 .checked_add(1)123 .ok_or(ArithmeticError::Overflow)124 .map(Self)125 }126}127128impl From<TokenId> for U256 {129 fn from(t: TokenId) -> Self {130 t.0.into()131 }132}133134impl TryFrom<U256> for TokenId {135 type Error = &'static str;136137 fn try_from(value: U256) -> Result<Self, Self::Error> {138 Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))139 }140}141142pub struct OverflowError;143impl From<OverflowError> for &'static str {144 fn from(_: OverflowError) -> Self {145 "overflow occured"146 }147}148149pub type DecimalPoints = u8;150151#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]152#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]153pub enum CollectionMode {154 NFT,155 // decimal points156 Fungible(DecimalPoints),157 ReFungible,158}159160impl CollectionMode {161 pub fn id(&self) -> u8 {162 match self {163 CollectionMode::NFT => 1,164 CollectionMode::Fungible(_) => 2,165 CollectionMode::ReFungible => 3,166 }167 }168}169170pub trait SponsoringResolve<AccountId, Call> {171 fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;172}173174#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]175#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]176pub enum AccessMode {177 Normal,178 AllowList,179}180impl Default for AccessMode {181 fn default() -> Self {182 Self::Normal183 }184}185186#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]187#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]188pub enum SchemaVersion {189 ImageURL,190 Unique,191}192impl Default for SchemaVersion {193 fn default() -> Self {194 Self::ImageURL195 }196}197198#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]199#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]200pub struct Ownership<AccountId> {201 pub owner: AccountId,202 pub fraction: u128,203}204205#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]206#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]207pub enum SponsorshipState<AccountId> {208 /// The fees are applied to the transaction sender209 Disabled,210 Unconfirmed(AccountId),211 /// Transactions are sponsored by specified account212 Confirmed(AccountId),213}214215impl<AccountId> SponsorshipState<AccountId> {216 pub fn sponsor(&self) -> Option<&AccountId> {217 match self {218 Self::Confirmed(sponsor) => Some(sponsor),219 _ => None,220 }221 }222223 pub fn pending_sponsor(&self) -> Option<&AccountId> {224 match self {225 Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),226 _ => None,227 }228 }229230 pub fn confirmed(&self) -> bool {231 matches!(self, Self::Confirmed(_))232 }233}234235impl<T> Default for SponsorshipState<T> {236 fn default() -> Self {237 Self::Disabled238 }239}240241#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]242#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]243pub struct Collection<AccountId> {244 pub owner: AccountId,245 pub mode: CollectionMode,246 pub access: AccessMode,247 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]248 pub name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,249 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]250 pub description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,251 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]252 pub token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,253 pub mint_mode: bool,254 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]255 pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,256 pub schema_version: SchemaVersion,257 pub sponsorship: SponsorshipState<AccountId>,258 pub limits: CollectionLimits, // Collection private restrictions259 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]260 pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,261 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]262 pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,263 pub meta_update_permission: MetaUpdatePermission,264}265266#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Debug, Derivative, MaxEncodedLen)]267#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]268#[derivative(Default(bound = ""))]269pub struct CreateCollectionData<AccountId> {270 #[derivative(Default(value = "CollectionMode::NFT"))]271 pub mode: CollectionMode,272 pub access: Option<AccessMode>,273 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]274 pub name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,275 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]276 pub description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,277 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]278 pub token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,279 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]280 pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,281 pub schema_version: Option<SchemaVersion>,282 pub pending_sponsor: Option<AccountId>,283 pub limits: Option<CollectionLimits>,284 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]285 pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,286 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]287 pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,288 pub meta_update_permission: Option<MetaUpdatePermission>,289}290291#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]292#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]293pub struct NftItemType<AccountId> {294 pub owner: AccountId,295 pub const_data: Vec<u8>,296 pub variable_data: Vec<u8>,297}298299#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]300#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]301pub struct FungibleItemType {302 pub value: u128,303}304305#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]306#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]307pub struct ReFungibleItemType<AccountId> {308 pub owner: Vec<Ownership<AccountId>>,309 pub const_data: Vec<u8>,310 pub variable_data: Vec<u8>,311}312313/// All fields are wrapped in `Option`s, where None means chain default314#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]315#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]316pub struct CollectionLimits {317 pub account_token_ownership_limit: Option<u32>,318 pub sponsored_data_size: Option<u32>,319 /// None - setVariableMetadata is not sponsored320 /// Some(v) - setVariableMetadata is sponsored321 /// if there is v block between txs322 pub sponsored_data_rate_limit: Option<(Option<u32>,)>,323 pub token_limit: Option<u32>,324325 // Timeouts for item types in passed blocks326 pub sponsor_transfer_timeout: Option<u32>,327 pub sponsor_approve_timeout: Option<u32>,328 pub owner_can_transfer: Option<bool>,329 pub owner_can_destroy: Option<bool>,330 pub transfers_enabled: Option<bool>,331}332333impl CollectionLimits {334 pub fn account_token_ownership_limit(&self) -> u32 {335 self.account_token_ownership_limit336 .unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)337 .min(MAX_TOKEN_OWNERSHIP)338 }339 pub fn sponsored_data_size(&self) -> u32 {340 self.sponsored_data_size341 .unwrap_or(CUSTOM_DATA_LIMIT)342 .min(CUSTOM_DATA_LIMIT)343 }344 pub fn token_limit(&self) -> u32 {345 self.token_limit346 .unwrap_or(COLLECTION_TOKEN_LIMIT)347 .min(COLLECTION_TOKEN_LIMIT)348 }349 pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {350 self.sponsor_transfer_timeout351 .unwrap_or(default)352 .min(MAX_SPONSOR_TIMEOUT)353 }354 pub fn sponsor_approve_timeout(&self) -> u32 {355 self.sponsor_approve_timeout356 .unwrap_or(SPONSOR_APPROVE_TIMEOUT)357 .min(MAX_SPONSOR_TIMEOUT)358 }359 pub fn owner_can_transfer(&self) -> bool {360 self.owner_can_transfer.unwrap_or(true)361 }362 pub fn owner_can_destroy(&self) -> bool {363 self.owner_can_destroy.unwrap_or(true)364 }365 pub fn transfers_enabled(&self) -> bool {366 self.transfers_enabled.unwrap_or(true)367 }368 pub fn sponsored_data_rate_limit(&self) -> Option<u32> {369 self.sponsored_data_rate_limit370 .unwrap_or((None,))371 .0372 .map(|v| v.min(MAX_SPONSOR_TIMEOUT))373 }374}375376/// BoundedVec doesn't supports serde377#[cfg(feature = "serde1")]378mod bounded_serde {379 use core::convert::TryFrom;380 use frame_support::{BoundedVec, traits::Get};381 use serde::{382 ser::{self, Serialize},383 de::{self, Deserialize, Error},384 };385 use sp_std::vec::Vec;386387 pub fn serialize<D, V, S>(value: &BoundedVec<V, S>, serializer: D) -> Result<D::Ok, D::Error>388 where389 D: ser::Serializer,390 V: Serialize,391 {392 (value as &Vec<_>).serialize(serializer)393 }394395 pub fn deserialize<'de, D, V, S>(deserializer: D) -> Result<BoundedVec<V, S>, D::Error>396 where397 D: de::Deserializer<'de>,398 V: de::Deserialize<'de>,399 S: Get<u32>,400 {401 // TODO: Implement custom visitor, which will limit vec size at parse time? Will serde only be used by chainspec?402 let vec = <Vec<V>>::deserialize(deserializer)?;403 let len = vec.len();404 TryFrom::try_from(vec).map_err(|_| D::Error::invalid_length(len, &"lesser size"))405 }406}407408#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]409#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]410#[derivative(Debug)]411pub struct CreateNftData {412 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]413 #[derivative(Debug = "ignore")]414 pub const_data: BoundedVec<u8, CustomDataLimit>,415 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]416 #[derivative(Debug = "ignore")]417 pub variable_data: BoundedVec<u8, CustomDataLimit>,418}419420#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]421#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]422pub struct CreateFungibleData {423 pub value: u128,424}425426#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]427#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]428#[derivative(Debug)]429pub struct CreateReFungibleData {430 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]431 #[derivative(Debug = "ignore")]432 pub const_data: BoundedVec<u8, CustomDataLimit>,433 #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]434 #[derivative(Debug = "ignore")]435 pub variable_data: BoundedVec<u8, CustomDataLimit>,436 pub pieces: u128,437}438439#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]440#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]441pub enum MetaUpdatePermission {442 ItemOwner,443 Admin,444 None,445}446447impl Default for MetaUpdatePermission {448 fn default() -> Self {449 Self::ItemOwner450 }451}452453#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]454#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]455pub enum CreateItemData {456 NFT(CreateNftData),457 Fungible(CreateFungibleData),458 ReFungible(CreateReFungibleData),459}460461impl CreateItemData {462 pub fn data_size(&self) -> usize {463 match self {464 CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),465 CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),466 _ => 0,467 }468 }469}470471impl From<CreateNftData> for CreateItemData {472 fn from(item: CreateNftData) -> Self {473 CreateItemData::NFT(item)474 }475}476477impl From<CreateReFungibleData> for CreateItemData {478 fn from(item: CreateReFungibleData) -> Self {479 CreateItemData::ReFungible(item)480 }481}482483impl From<CreateFungibleData> for CreateItemData {484 fn from(item: CreateFungibleData) -> Self {485 CreateItemData::Fungible(item)486 }487}488489#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]490#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]491pub struct CollectionStats {492 pub created: u32,493 pub destroyed: u32,494 pub alive: u32,495}primitives/rpc/src/lib.rsdiffbeforeafterboth--- a/primitives/rpc/src/lib.rs
+++ b/primitives/rpc/src/lib.rs
@@ -9,10 +9,14 @@
type Result<T> = core::result::Result<T, DispatchError>;
sp_api::decl_runtime_apis! {
+ #[api_version(2)]
pub trait UniqueApi<CrossAccountId, AccountId> where
AccountId: Decode,
CrossAccountId: pallet_common::account::CrossAccountId<AccountId>,
{
+ #[changed_in(2)]
+ fn token_owner(collection: CollectionId, token: TokenId) -> Result<CrossAccountId>;
+
fn account_tokens(collection: CollectionId, account: CrossAccountId) -> Result<Vec<TokenId>>;
fn token_exists(collection: CollectionId, token: TokenId) -> Result<bool>;
runtime/src/lib.rsdiffbeforeafterboth--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -23,7 +23,8 @@
use sp_runtime::{
Permill, Perbill, Percent, create_runtime_str, generic, impl_opaque_keys,
traits::{
- AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, Verify, AccountIdConversion,
+ AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, Verify,
+ AccountIdConversion, Zero,
},
transaction_validity::{TransactionSource, TransactionValidity},
ApplyExtrinsicResult, MultiSignature, RuntimeAppPublic,
@@ -45,8 +46,9 @@
dispatch::DispatchResult,
PalletId, parameter_types, StorageValue, ConsensusEngineId,
traits::{
- Everything, Currency, ExistenceRequirement, Get, IsInVec, KeyOwnerProofSystem,
- LockIdentifier, OnUnbalanced, Randomness, FindAuthor,
+ tokens::currency::Currency as CurrencyT, OnUnbalanced as OnUnbalancedT, Everything,
+ Currency, ExistenceRequirement, Get, IsInVec, KeyOwnerProofSystem, LockIdentifier,
+ OnUnbalanced, Randomness, FindAuthor,
},
weights::{
constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},
@@ -69,8 +71,9 @@
use pallet_evm::{Account as EVMAccount, FeeCalculator, GasWeightMapping, OnMethodCall};
use fp_rpc::TransactionStatus;
use sp_runtime::{
- traits::{BlockNumberProvider, Dispatchable, PostDispatchInfoOf},
+ traits::{BlockNumberProvider, Dispatchable, PostDispatchInfoOf, Saturating},
transaction_validity::TransactionValidityError,
+ SaturatedConversion,
};
// pub use pallet_timestamp::Call as TimestampCall;
@@ -82,13 +85,24 @@
use xcm::v1::{BodyId, Junction::*, MultiLocation, NetworkId, Junctions::*};
use xcm_builder::{
AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter,
- EnsureXcmOrigin, FixedWeightBounds, IsConcrete, LocationInverter, NativeAsset,
- ParentAsSuperuser, ParentIsDefault, RelayChainAsNative, SiblingParachainAsNative,
- SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32,
- SovereignSignedViaLocation, TakeWeightCredit, UsingComponents,
+ EnsureXcmOrigin, FixedWeightBounds, LocationInverter, NativeAsset, ParentAsSuperuser,
+ ParentIsDefault, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia,
+ SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit,
};
-use xcm_executor::{Config, XcmExecutor};
+use xcm_executor::{Config, XcmExecutor, Assets};
+use sp_std::{marker::PhantomData};
+use xcm::latest::{
+ // Xcm,
+ AssetId::{Concrete},
+ Fungibility::Fungible as XcmFungible,
+ MultiAsset,
+ Error as XcmError,
+};
+use xcm_executor::traits::{MatchesFungible, WeightTrader};
+//use xcm_executor::traits::MatchesFungible;
+use sp_runtime::traits::CheckedConversion;
+
// mod chain_extension;
// use crate::chain_extension::{NFTExtension, Imbalance};
@@ -146,10 +160,10 @@
spec_name: create_runtime_str!("opal"),
impl_name: create_runtime_str!("opal"),
authoring_version: 1,
- spec_version: 914000,
- impl_version: 1,
+ spec_version: 916001,
+ impl_version: 0,
apis: RUNTIME_API_VERSIONS,
- transaction_version: 0,
+ transaction_version: 1,
state_version: 0,
};
@@ -616,12 +630,22 @@
AccountId32Aliases<RelayNetwork, AccountId>,
);
+pub struct OnlySelfCurrency;
+impl<B: TryFrom<u128>> MatchesFungible<B> for OnlySelfCurrency {
+ fn matches_fungible(a: &MultiAsset) -> Option<B> {
+ match (&a.id, &a.fun) {
+ (Concrete(_), XcmFungible(ref amount)) => CheckedConversion::checked_from(*amount),
+ _ => None,
+ }
+ }
+}
+
/// Means for transacting assets on this chain.
pub type LocalAssetTransactor = CurrencyAdapter<
// Use this currency:
Balances,
// Use this currency when it is a fungible asset matching the given location or name:
- IsConcrete<RelayLocation>,
+ OnlySelfCurrency,
// Do a simple punn to convert an AccountId32 MultiLocation into a native chain account ID:
LocationToAccountId,
// Our chain's account ID type (we can't get away without mentioning it explicitly):
@@ -677,6 +701,88 @@
// ^^^ Parent & its unit plurality gets free execution
);
+pub struct UsingOnlySelfCurrencyComponents<
+ WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,
+ AssetId: Get<MultiLocation>,
+ AccountId,
+ Currency: CurrencyT<AccountId>,
+ OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,
+>(
+ Weight,
+ Currency::Balance,
+ PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>,
+);
+impl<
+ WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,
+ AssetId: Get<MultiLocation>,
+ AccountId,
+ Currency: CurrencyT<AccountId>,
+ OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,
+ > WeightTrader
+ for UsingOnlySelfCurrencyComponents<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>
+{
+ fn new() -> Self {
+ Self(0, Zero::zero(), PhantomData)
+ }
+
+ fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, XcmError> {
+ let amount = WeightToFee::calc(&weight);
+ let u128_amount: u128 = amount.try_into().map_err(|_| XcmError::Overflow)?;
+
+ // location to this parachain through relay chain
+ let option1: xcm::v1::AssetId = Concrete(MultiLocation {
+ parents: 1,
+ interior: X1(Parachain(ParachainInfo::parachain_id().into())),
+ });
+ // direct location
+ let option2: xcm::v1::AssetId = Concrete(MultiLocation {
+ parents: 0,
+ interior: Here,
+ });
+
+ let required = if payment.fungible.contains_key(&option1) {
+ (option1, u128_amount).into()
+ } else if payment.fungible.contains_key(&option2) {
+ (option2, u128_amount).into()
+ } else {
+ (Concrete(MultiLocation::default()), u128_amount).into()
+ };
+
+ let unused = payment
+ .checked_sub(required)
+ .map_err(|_| XcmError::TooExpensive)?;
+ self.0 = self.0.saturating_add(weight);
+ self.1 = self.1.saturating_add(amount);
+ Ok(unused)
+ }
+
+ fn refund_weight(&mut self, weight: Weight) -> Option<MultiAsset> {
+ let weight = weight.min(self.0);
+ let amount = WeightToFee::calc(&weight);
+ self.0 -= weight;
+ self.1 = self.1.saturating_sub(amount);
+ let amount: u128 = amount.saturated_into();
+ if amount > 0 {
+ Some((AssetId::get(), amount).into())
+ } else {
+ None
+ }
+ }
+}
+impl<
+ WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,
+ AssetId: Get<MultiLocation>,
+ AccountId,
+ Currency: CurrencyT<AccountId>,
+ OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,
+ > Drop
+ for UsingOnlySelfCurrencyComponents<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>
+{
+ fn drop(&mut self) {
+ OnUnbalanced::on_unbalanced(Currency::issue(self.1));
+ }
+}
+
pub struct XcmConfig;
impl Config for XcmConfig {
type Call = Call;
@@ -689,7 +795,13 @@
type LocationInverter = LocationInverter<Ancestry>;
type Barrier = Barrier;
type Weigher = FixedWeightBounds<UnitWeightCost, Call, MaxInstructions>;
- type Trader = UsingComponents<IdentityFee<Balance>, RelayLocation, AccountId, Balances, ()>;
+ type Trader = UsingOnlySelfCurrencyComponents<
+ IdentityFee<Balance>,
+ RelayLocation,
+ AccountId,
+ Balances,
+ (),
+ >;
type ResponseHandler = (); // Don't handle responses for now.
type SubscriptionService = PolkadotXcm;
tests/package.jsondiffbeforeafterboth--- a/tests/package.json
+++ b/tests/package.json
@@ -64,12 +64,14 @@
"testOverflow": "mocha --timeout 9999999 -r ts-node/register ./**/overflow.test.ts",
"testSetVariableMetadataSponsoringRateLimit": "mocha --timeout 9999999 -r ts-node/register ./**/setVariableMetadataSponsoringRateLimit.test.ts",
"testInflation": "mocha --timeout 9999999 -r ts-node/register ./**/inflation.test.ts",
+ "testXcmTransfer": "mocha --timeout 9999999 -r ts-node/register ./**/xcmTransfer.test.ts",
"testPalletPresence": "mocha --timeout 9999999 -r ts-node/register ./**/pallet-presence.test.ts",
"testBlockProduction": "mocha --timeout 9999999 -r ts-node/register ./**/block-production.test.ts",
"testEnableDisableTransfers": "mocha --timeout 9999999 -r ts-node/register ./**/enableDisableTransfer.test.ts",
+ "polkadot-types-fetch-metadata": "curl -H 'Content-Type: application/json' -d '{\"id\":\"1\", \"jsonrpc\":\"2.0\", \"method\": \"state_getMetadata\", \"params\":[]}' http://localhost:9933 > src/interfaces/metadata.json",
"polkadot-types-from-defs": "ts-node ./node_modules/.bin/polkadot-types-from-defs --input src/interfaces/ --package .",
- "polkadot-types-from-chain": "ts-node ./node_modules/.bin/polkadot-types-from-chain --endpoint ws://localhost:9944 --output src/interfaces/ --package .",
- "polkadot-types": "yarn polkadot-types-from-defs && yarn polkadot-types-from-chain"
+ "polkadot-types-from-chain": "ts-node ./node_modules/.bin/polkadot-types-from-chain --endpoint src/interfaces/metadata.json --output src/interfaces/ --package .",
+ "polkadot-types": "yarn polkadot-types-fetch-metadata && yarn polkadot-types-from-defs && yarn polkadot-types-from-chain"
},
"author": "",
"license": "SEE LICENSE IN ../LICENSE",
tests/src/check-event/createCollectionEvent.test.tsdiffbeforeafterboth--- a/tests/src/check-event/createCollectionEvent.test.ts
+++ b/tests/src/check-event/createCollectionEvent.test.ts
@@ -27,7 +27,7 @@
});
it('Check event from createCollection(): ', async () => {
await usingApi(async (api: ApiPromise) => {
- const tx = api.tx.unique.createCollection([0x31], [0x32], '0x33', 'NFT');
+ const tx = api.tx.unique.createCollectionEx({name: [0x31], description: [0x32], tokenPrefix: '0x33', mode: 'NFT'});
const events = await submitTransactionAsync(alice, tx);
const msg = JSON.stringify(uniqueEventMessage(events));
expect(msg).to.be.contain(checkSection);
tests/src/createCollection.test.tsdiffbeforeafterboth--- a/tests/src/createCollection.test.ts
+++ b/tests/src/createCollection.test.ts
@@ -3,12 +3,11 @@
// file 'LICENSE', which is part of this source code package.
//
-import chai from 'chai';
-import chaiAsPromised from 'chai-as-promised';
-import {createCollectionExpectFailure, createCollectionExpectSuccess} from './util/helpers';
+import {expect} from 'chai';
+import privateKey from './substrate/privateKey';
+import usingApi, {executeTransaction, submitTransactionAsync} from './substrate/substrate-api';
+import {createCollectionExpectFailure, createCollectionExpectSuccess, getCreateCollectionResult, getDetailedCollectionInfo} from './util/helpers';
-chai.use(chaiAsPromised);
-
describe('integration test: ext. createCollection():', () => {
it('Create new NFT collection', async () => {
await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}});
@@ -28,6 +27,45 @@
it('Create new ReFungible collection', async () => {
await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
});
+ it('Create new collection with extra fields', async () => {
+ await usingApi(async api => {
+ const alice = privateKey('//Alice');
+ const bob = privateKey('//Bob');
+ const tx = api.tx.unique.createCollectionEx({
+ mode: {Fungible: 8},
+ access: 'AllowList',
+ name: [1],
+ description: [2],
+ tokenPrefix: '0x000000',
+ offchainSchema: '0x111111',
+ schemaVersion: 'Unique',
+ pendingSponsor: bob.address,
+ limits: {
+ accountTokenOwnershipLimit: 3,
+ },
+ variableOnChainSchema: '0x222222',
+ constOnChainSchema: '0x333333',
+ metaUpdatePermission: 'Admin',
+ });
+ const events = await submitTransactionAsync(alice, tx);
+ const result = getCreateCollectionResult(events);
+
+ const collection = (await getDetailedCollectionInfo(api, result.collectionId))!;
+ expect(collection.owner.toString()).to.equal(alice.address);
+ expect(collection.mode.asFungible.toNumber()).to.equal(8);
+ expect(collection.access.isAllowList).to.be.true;
+ expect(collection.name.map(v => v.toNumber())).to.deep.equal([1]);
+ expect(collection.description.map(v => v.toNumber())).to.deep.equal([2]);
+ expect(collection.tokenPrefix.toString()).to.equal('0x000000');
+ expect(collection.offchainSchema.toString()).to.equal('0x111111');
+ expect(collection.schemaVersion.isUnique).to.be.true;
+ expect(collection.sponsorship.asUnconfirmed.toString()).to.equal(bob.address);
+ expect(collection.limits.accountTokenOwnershipLimit.unwrap().toNumber()).to.equal(3);
+ expect(collection.variableOnChainSchema.toString()).to.equal('0x222222');
+ expect(collection.constOnChainSchema.toString()).to.equal('0x333333');
+ expect(collection.metaUpdatePermission.isAdmin).to.be.true;
+ });
+ });
});
describe('(!negative test!) integration test: ext. createCollection():', () => {
@@ -40,4 +78,11 @@
it('(!negative test!) create new NFT collection whith incorrect data (token_prefix)', async () => {
await createCollectionExpectFailure({tokenPrefix: 'A'.repeat(17), mode: {type: 'NFT'}});
});
+ it('fails when bad limits are set', async () => {
+ await usingApi(async api => {
+ const alice = privateKey('//Alice');
+ const tx = api.tx.unique.createCollectionEx({mode: 'NFT', limits: {tokenLimit: 0}});
+ await expect(executeTransaction(api, alice, tx)).to.be.rejectedWith(/^common.CollectionTokenLimitExceeded$/);
+ });
+ });
});
tests/src/interfaces/.gitignorediffbeforeafterboth--- /dev/null
+++ b/tests/src/interfaces/.gitignore
@@ -0,0 +1 @@
+metadata.json
tests/src/interfaces/augment-api-errors.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-errors.ts
+++ b/tests/src/interfaces/augment-api-errors.ts
@@ -69,6 +69,10 @@
**/
CollectionDescriptionLimitExceeded: AugmentedError<ApiType>;
/**
+ * Collection limit bounds per collection exceeded
+ **/
+ CollectionLimitBoundsExceeded: AugmentedError<ApiType>;
+ /**
* Collection name can not be longer than 63 char.
**/
CollectionNameLimitExceeded: AugmentedError<ApiType>;
@@ -97,6 +101,10 @@
**/
NoPermission: AugmentedError<ApiType>;
/**
+ * Tried to enable permissions which are only permitted to be disabled
+ **/
+ OwnerPermissionsCantBeReverted: AugmentedError<ApiType>;
+ /**
* Collection is not in mint mode.
**/
PublicMintingNotAllowed: AugmentedError<ApiType>;
@@ -107,7 +115,7 @@
/**
* Requested value more than approved.
**/
- TokenValueNotEnough: AugmentedError<ApiType>;
+ ApprovedValueTooLow: AugmentedError<ApiType>;
/**
* Item balance not enough.
**/
@@ -436,10 +444,6 @@
* Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.
**/
CollectionDecimalPointLimitExceeded: AugmentedError<ApiType>;
- /**
- * Collection limit bounds per collection exceeded
- **/
- CollectionLimitBoundsExceeded: AugmentedError<ApiType>;
/**
* This address is not set as sponsor, use setCollectionSponsor first.
**/
@@ -448,10 +452,6 @@
* Length of items properties must be greater than 0.
**/
EmptyArgument: AugmentedError<ApiType>;
- /**
- * Tried to enable permissions which are only permitted to be disabled
- **/
- OwnerPermissionsCantBeReverted: AugmentedError<ApiType>;
/**
* Generic error
**/
tests/src/interfaces/augment-api-tx.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-tx.ts
+++ b/tests/src/interfaces/augment-api-tx.ts
@@ -2,7 +2,7 @@
/* eslint-disable */
import type { CumulusPrimitivesParachainInherentParachainInherentData } from './polkadot';
-import type { PalletCommonAccountBasicCrossAccountIdRepr, UpDataStructsAccessMode, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCreateItemData, UpDataStructsMetaUpdatePermission, UpDataStructsSchemaVersion } from './unique';
+import type { PalletCommonAccountBasicCrossAccountIdRepr, UpDataStructsAccessMode, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCreateCollectionData, UpDataStructsCreateItemData, UpDataStructsMetaUpdatePermission, UpDataStructsSchemaVersion } from './unique';
import type { ApiTypes, SubmittableExtrinsic } from '@polkadot/api/types';
import type { Bytes, Compact, Option, U256, Vec, bool, u128, u16, u32, u64 } from '@polkadot/types';
import type { Extrinsic } from '@polkadot/types/interfaces/extrinsics';
@@ -671,6 +671,12 @@
**/
createCollection: AugmentedSubmittable<(collectionName: Vec<u16> | (u16 | AnyNumber | Uint8Array)[], collectionDescription: Vec<u16> | (u16 | AnyNumber | Uint8Array)[], tokenPrefix: Bytes | string | Uint8Array, mode: UpDataStructsCollectionMode | { NFT: any } | { Fungible: any } | { ReFungible: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Vec<u16>, Vec<u16>, Bytes, UpDataStructsCollectionMode]>;
/**
+ * This method creates a collection
+ *
+ * Prefer it to deprecated [`created_collection`] method
+ **/
+ createCollectionEx: AugmentedSubmittable<(data: UpDataStructsCreateCollectionData | { mode?: any; access?: any; name?: any; description?: any; tokenPrefix?: any; offchainSchema?: any; schemaVersion?: any; pendingSponsor?: any; limits?: any; variableOnChainSchema?: any; constOnChainSchema?: any; metaUpdatePermission?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [UpDataStructsCreateCollectionData]>;
+ /**
* This method creates a concrete instance of NFT Collection created with CreateCollection method.
*
* # Permissions
tests/src/interfaces/augment-types.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-types.ts
+++ b/tests/src/interfaces/augment-types.ts
@@ -3,7 +3,7 @@
import type { EthereumBlock, EthereumLog, EthereumReceipt, EthereumTransactionLegacyTransaction, EvmCoreErrorExitReason, FpRpcTransactionStatus } from './ethereum';
import type { CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmpQueueInboundStatus, CumulusPalletXcmpQueueOutboundStatus, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV1AbridgedHostConfiguration, PolkadotPrimitivesV1PersistedValidationData } from './polkadot';
-import type { PalletCommonAccountBasicCrossAccountIdRepr, PalletNonfungibleItemData, PalletRefungibleItemData, PalletUnqSchedulerCallSpec, PalletUnqSchedulerReleases, PalletUnqSchedulerScheduledV2, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionId, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionStats, UpDataStructsCreateItemData, UpDataStructsMetaUpdatePermission, UpDataStructsSchemaVersion, UpDataStructsSponsorshipState, UpDataStructsTokenId } from './unique';
+import type { PalletCommonAccountBasicCrossAccountIdRepr, PalletNonfungibleItemData, PalletRefungibleItemData, PalletUnqSchedulerCallSpec, PalletUnqSchedulerReleases, PalletUnqSchedulerScheduledV2, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionId, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateItemData, UpDataStructsMetaUpdatePermission, UpDataStructsSchemaVersion, UpDataStructsSponsorshipState, UpDataStructsTokenId } from './unique';
import type { BitVec, Bool, Bytes, Data, I128, I16, I256, I32, I64, I8, Json, Null, Raw, StorageKey, Text, Type, U128, U16, U256, U32, U64, U8, USize, bool, i128, i16, i256, i32, i64, i8, u128, u16, u256, u32, u64, u8, usize } from '@polkadot/types';
import type { AssetApproval, AssetApprovalKey, AssetBalance, AssetDestroyWitness, AssetDetails, AssetMetadata, TAssetBalance, TAssetDepositBalance } from '@polkadot/types/interfaces/assets';
import type { BlockAttestations, IncludedBlocks, MoreAttestations } from '@polkadot/types/interfaces/attestations';
@@ -1021,6 +1021,7 @@
UpDataStructsCollectionLimits: UpDataStructsCollectionLimits;
UpDataStructsCollectionMode: UpDataStructsCollectionMode;
UpDataStructsCollectionStats: UpDataStructsCollectionStats;
+ UpDataStructsCreateCollectionData: UpDataStructsCreateCollectionData;
UpDataStructsCreateItemData: UpDataStructsCreateItemData;
UpDataStructsMetaUpdatePermission: UpDataStructsMetaUpdatePermission;
UpDataStructsSchemaVersion: UpDataStructsSchemaVersion;
tests/src/interfaces/unique/definitions.tsdiffbeforeafterboth--- a/tests/src/interfaces/unique/definitions.ts
+++ b/tests/src/interfaces/unique/definitions.ts
@@ -67,6 +67,20 @@
constOnChainSchema: 'Vec<u8>',
metaUpdatePermission: 'UpDataStructsMetaUpdatePermission',
},
+ UpDataStructsCreateCollectionData: {
+ mode: 'UpDataStructsCollectionMode',
+ access: 'Option<UpDataStructsAccessMode>',
+ name: 'Vec<u16>',
+ description: 'Vec<u16>',
+ tokenPrefix: 'Vec<u8>',
+ offchainSchema: 'Vec<u8>',
+ schemaVersion: 'Option<UpDataStructsSchemaVersion>',
+ pendingSponsor: 'Option<AccountId>',
+ limits: 'Option<UpDataStructsCollectionLimits>',
+ variableOnChainSchema: 'Vec<u8>',
+ constOnChainSchema: 'Vec<u8>',
+ metaUpdatePermission: 'Option<UpDataStructsMetaUpdatePermission>',
+ },
UpDataStructsCollectionStats: {
created: 'u32',
destroyed: 'u32',
@@ -76,7 +90,13 @@
UpDataStructsTokenId: 'u32',
PalletNonfungibleItemData: mkDummy('NftItemData'),
PalletRefungibleItemData: mkDummy('RftItemData'),
- UpDataStructsCollectionMode: mkDummy('CollectionMode'),
+ UpDataStructsCollectionMode: {
+ _enum: {
+ NFT: null,
+ Fungible: 'u32',
+ ReFungible: null,
+ },
+ },
UpDataStructsCreateItemData: mkDummy('CreateItemData'),
UpDataStructsCollectionLimits: {
accountTokenOwnershipLimit: 'Option<u32>',
@@ -101,7 +121,9 @@
UpDataStructsAccessMode: {
_enum: ['Normal', 'AllowList'],
},
- UpDataStructsSchemaVersion: mkDummy('SchemaVersion'),
+ UpDataStructsSchemaVersion: {
+ _enum: ['ImageURL', 'Unique'],
+ },
PalletUnqSchedulerScheduledV2: mkDummy('ScheduledV2'),
PalletUnqSchedulerCallSpec: mkDummy('CallSpec'),
tests/src/interfaces/unique/types.tsdiffbeforeafterboth--- a/tests/src/interfaces/unique/types.ts
+++ b/tests/src/interfaces/unique/types.ts
@@ -77,8 +77,11 @@
}
/** @name UpDataStructsCollectionMode */
-export interface UpDataStructsCollectionMode extends Struct {
- readonly dummyCollectionMode: u32;
+export interface UpDataStructsCollectionMode extends Enum {
+ readonly isNft: boolean;
+ readonly isFungible: boolean;
+ readonly asFungible: u32;
+ readonly isReFungible: boolean;
}
/** @name UpDataStructsCollectionStats */
@@ -88,6 +91,22 @@
readonly alive: u32;
}
+/** @name UpDataStructsCreateCollectionData */
+export interface UpDataStructsCreateCollectionData extends Struct {
+ readonly mode: UpDataStructsCollectionMode;
+ readonly access: Option<UpDataStructsAccessMode>;
+ readonly name: Vec<u16>;
+ readonly description: Vec<u16>;
+ readonly tokenPrefix: Bytes;
+ readonly offchainSchema: Bytes;
+ readonly schemaVersion: Option<UpDataStructsSchemaVersion>;
+ readonly pendingSponsor: Option<AccountId>;
+ readonly limits: Option<UpDataStructsCollectionLimits>;
+ readonly variableOnChainSchema: Bytes;
+ readonly constOnChainSchema: Bytes;
+ readonly metaUpdatePermission: Option<UpDataStructsMetaUpdatePermission>;
+}
+
/** @name UpDataStructsCreateItemData */
export interface UpDataStructsCreateItemData extends Struct {
readonly dummyCreateItemData: u32;
@@ -101,8 +120,9 @@
}
/** @name UpDataStructsSchemaVersion */
-export interface UpDataStructsSchemaVersion extends Struct {
- readonly dummySchemaVersion: u32;
+export interface UpDataStructsSchemaVersion extends Enum {
+ readonly isImageUrl: boolean;
+ readonly isUnique: boolean;
}
/** @name UpDataStructsSponsorshipState */
tests/src/substrate/substrate-api.tsdiffbeforeafterboth--- a/tests/src/substrate/substrate-api.ts
+++ b/tests/src/substrate/substrate-api.ts
@@ -95,6 +95,32 @@
return TransactionStatus.Fail;
}
+export function executeTransaction(api: ApiPromise, sender: IKeyringPair, transaction: SubmittableExtrinsic<'promise'>): Promise<EventRecord[]> {
+ return new Promise(async (res, rej) => {
+ try {
+ await transaction.signAndSend(sender, ({events, status}) => {
+ if (!status.isInBlock && !status.isFinalized) return;
+ for (const {event} of events) {
+ if (api.events.system.ExtrinsicSuccess.is(event)) {
+ res(events);
+ } else if (api.events.system.ExtrinsicFailed.is(event)) {
+ const {data: [error]} = event;
+ if (error.isModule) {
+ const decoded = api.registry.findMetaError(error.asModule);
+ const {method, section} = decoded;
+ rej(new Error(`${section}.${method}`));
+ } else {
+ rej(new Error(error.toString()));
+ }
+ }
+ }
+ });
+ } catch (e) {
+ rej(e);
+ }
+ });
+}
+
export function
submitTransactionAsync(sender: IKeyringPair, transaction: SubmittableExtrinsic<ApiTypes>): Promise<EventRecord[]> {
/* eslint no-async-promise-executor: "off" */
tests/src/util/helpers.tsdiffbeforeafterboth--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -49,7 +49,7 @@
};
} else if ('Substrate' in input) {
return input;
- }else if ('substrate' in input) {
+ } else if ('substrate' in input) {
return {
Substrate: (input as any).substrate,
};
@@ -116,15 +116,15 @@
export interface IChainLimits {
collectionNumbersLimit: number;
- accountTokenOwnershipLimit: number;
- collectionsAdminsLimit: number;
- customDataLimit: number;
- nftSponsorTransferTimeout: number;
- fungibleSponsorTransferTimeout: number;
- refungibleSponsorTransferTimeout: number;
- offchainSchemaLimit: number;
- variableOnChainSchemaLimit: number;
- constOnChainSchemaLimit: number;
+ accountTokenOwnershipLimit: number;
+ collectionsAdminsLimit: number;
+ customDataLimit: number;
+ nftSponsorTransferTimeout: number;
+ fungibleSponsorTransferTimeout: number;
+ refungibleSponsorTransferTimeout: number;
+ offchainSchemaLimit: number;
+ variableOnChainSchemaLimit: number;
+ constOnChainSchemaLimit: number;
}
export interface IReFungibleTokenDataType {
@@ -283,7 +283,7 @@
modeprm = {refungible: null};
}
- const tx = api.tx.unique.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), modeprm as any);
+ const tx = api.tx.unique.createCollectionEx({name: strToUTF16(name), description: strToUTF16(description), tokenPrefix: strToUTF16(tokenPrefix), mode: modeprm as any});
const events = await submitTransactionAsync(alicePrivateKey, tx);
const result = getCreateCollectionResult(events);
@@ -329,7 +329,7 @@
// Run the CreateCollection transaction
const alicePrivateKey = privateKey('//Alice');
- const tx = api.tx.unique.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), modeprm as any);
+ const tx = api.tx.unique.createCollectionEx({name: strToUTF16(name), description: strToUTF16(description), tokenPrefix: strToUTF16(tokenPrefix), mode: modeprm as any});
const events = await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;
const result = getCreateCollectionResult(events);
@@ -557,7 +557,7 @@
await usingApi(async (api) => {
- const tx = api.tx.unique.setTransfersEnabledFlag (collectionId, enabled);
+ const tx = api.tx.unique.setTransfersEnabledFlag(collectionId, enabled);
const events = await submitTransactionAsync(sender, tx);
const result = getGenericResult(events);
@@ -569,7 +569,7 @@
await usingApi(async (api) => {
- const tx = api.tx.unique.setTransfersEnabledFlag (collectionId, enabled);
+ const tx = api.tx.unique.setTransfersEnabledFlag(collectionId, enabled);
const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;
const result = getGenericResult(events);
@@ -811,8 +811,7 @@
}
export async function
-getFreeBalance(account: IKeyringPair) : Promise<bigint>
-{
+getFreeBalance(account: IKeyringPair): Promise<bigint> {
let balance = 0n;
await usingApi(async (api) => {
balance = BigInt((await api.query.system.account(account.address)).data.free.toString());
tests/src/xcmTransfer.test.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/xcmTransfer.test.ts
@@ -0,0 +1,178 @@
+//
+// 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 {WsProvider} from '@polkadot/api';
+import {ApiOptions} from '@polkadot/api/types';
+import {IKeyringPair} from '@polkadot/types/types';
+import privateKey from './substrate/privateKey';
+import usingApi, {submitTransactionAsync} from './substrate/substrate-api';
+import {getGenericResult} from './util/helpers';
+import waitNewBlocks from './substrate/wait-new-blocks';
+import getBalance from './substrate/get-balance';
+import {alicesPublicKey} from './accounts';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+const UNIQUE_CHAIN = 1000;
+const KARURA_CHAIN = 2000;
+const KARURA_PORT = '9946';
+
+describe('Integration test: Exchanging OPL with Karura', () => {
+ let alice: IKeyringPair;
+
+ before(async () => {
+ await usingApi(async () => {
+ alice = privateKey('//Alice');
+ });
+
+ const karuraApiOptions: ApiOptions = {
+ provider: new WsProvider('ws://127.0.0.1:' + KARURA_PORT),
+ };
+
+ await usingApi(async (api) => {
+ const destination = {
+ V0: {
+ X2: [
+ 'Parent',
+ {
+ Parachain: UNIQUE_CHAIN,
+ },
+ ],
+ },
+ };
+
+ const metadata =
+ {
+ name: 'OPL',
+ symbol: 'OPL',
+ decimals: 18,
+ minimalBalance: 1,
+ };
+
+ const tx = api.tx.assetRegistry.registerForeignAsset(destination, metadata);
+ const sudoTx = api.tx.sudo.sudo(tx as any);
+ const events = await submitTransactionAsync(alice, sudoTx);
+ const result = getGenericResult(events);
+ expect(result.success).to.be.true;
+ }, karuraApiOptions);
+ });
+
+ it('Should connect and send OPL to Karura', async () => {
+ let balanceOnKaruraBefore: bigint;
+
+ await usingApi(async (api) => {
+ const {free} = (await api.query.tokens.accounts(alice.addressRaw, {ForeignAsset: 0})).toJSON() as any;
+ balanceOnKaruraBefore = free;
+ }, {provider: new WsProvider('ws://127.0.0.1:' + KARURA_PORT)});
+
+ await usingApi(async (api) => {
+ const destination = {
+ V0: {
+ X2: [
+ 'Parent',
+ {
+ Parachain: KARURA_CHAIN,
+ },
+ ],
+ },
+ };
+
+ const beneficiary = {
+ V0: {
+ X1: {
+ AccountId32: {
+ network: 'Any',
+ id: alice.addressRaw,
+ },
+ },
+ },
+ };
+
+ const assets = {
+ V1: [
+ {
+ id: {
+ Concrete: {
+ parents: 0,
+ interior: 'Here',
+ },
+ },
+ fun: {
+ Fungible: 5000000000,
+ },
+ },
+ ],
+ };
+
+ const feeAssetItem = 0;
+
+ const weightLimit = {
+ Limited: 5000000000,
+ };
+
+ const tx = api.tx.polkadotXcm.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit);
+ const events = await submitTransactionAsync(alice, tx);
+ const result = getGenericResult(events);
+ expect(result.success).to.be.true;
+ });
+
+ await usingApi(async (api) => {
+ // todo do something about instant sealing, where there might not be any new blocks
+ await waitNewBlocks(api, 3);
+ const {free} = (await api.query.tokens.accounts(alice.addressRaw, {ForeignAsset: 0})).toJSON() as any;
+ expect(free > balanceOnKaruraBefore).to.be.true;
+ }, {provider: new WsProvider('ws://127.0.0.1:' + KARURA_PORT)});
+ });
+
+ it('Should connect to Karura and send OPL back', async () => {
+ let balanceBefore: bigint;
+
+ await usingApi(async (api) => {
+ [balanceBefore] = await getBalance(api, [alicesPublicKey]);
+ });
+
+ await usingApi(async (api) => {
+ const destination = {
+ V0: {
+ X3: [
+ 'Parent',
+ {
+ Parachain: UNIQUE_CHAIN,
+ },
+ {
+ AccountId32: {
+ network: 'Any',
+ id: alice.addressRaw,
+ },
+ },
+ ],
+ },
+ };
+
+ const id = {
+ ForeignAsset: 0,
+ };
+
+ const amount = 5000000000;
+ const destWeight = 50000000;
+
+ const tx = api.tx.xTokens.transfer(id, amount, destination, destWeight);
+ const events = await submitTransactionAsync(alice, tx);
+ const result = getGenericResult(events);
+ expect(result.success).to.be.true;
+ }, {provider: new WsProvider('ws://127.0.0.1:' + KARURA_PORT)});
+
+ await usingApi(async (api) => {
+ // todo do something about instant sealing, where there might not be any new blocks
+ await waitNewBlocks(api, 3);
+ const [balanceAfter] = await getBalance(api, [alicesPublicKey]);
+ expect(balanceAfter > balanceBefore).to.be.true;
+ });
+ });
+});
\ No newline at end of file