difftreelog
merged
in: master
46 files changed
.docker/Dockerfile-chain-dev-unitdiffbeforeafterboth--- a/.docker/Dockerfile-chain-dev-unit
+++ b/.docker/Dockerfile-chain-dev-unit
@@ -23,4 +23,4 @@
WORKDIR /dev_chain
-CMD cargo test --features=limit-testing
+CMD cargo test --features=limit-testing --workspace
.docker/Dockerfile-testnet.j2diffbeforeafterboth--- /dev/null
+++ b/.docker/Dockerfile-testnet.j2
@@ -0,0 +1,75 @@
+# ===== Rust builder =====
+FROM ubuntu:20.04 as rust-builder
+LABEL maintainer="Unique.Network"
+
+ENV CARGO_HOME="/cargo-home"
+ENV PATH="/cargo-home/bin:$PATH"
+ENV TZ=UTC
+RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
+
+RUN apt-get update && \
+ apt-get install -y curl cmake pkg-config libssl-dev git clang llvm libudev-dev && \
+ apt-get clean && \
+ rm -r /var/lib/apt/lists/*
+
+RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain none
+
+RUN rustup toolchain uninstall $(rustup toolchain list) && \
+ rustup toolchain install {{ RUST_TOOLCHAIN }} && \
+ rustup default {{ RUST_TOOLCHAIN }} && \
+ rustup target list --installed && \
+ rustup show
+RUN rustup target add wasm32-unknown-unknown --toolchain {{ RUST_TOOLCHAIN }}
+
+RUN mkdir /unique_parachain
+WORKDIR /unique_parachain
+
+# ===== BUILD ======
+FROM rust-builder as builder-unique
+
+ARG PROFILE=release
+
+WORKDIR /unique_parachain
+
+RUN git clone -b {{ BRANCH }} https://github.com/UniqueNetwork/unique-chain.git && \
+ cd unique-chain && \
+ cargo build --features={{ FEATURE }} --$PROFILE
+
+# ===== RUN ======
+
+FROM ubuntu:20.04
+
+RUN apt-get -y update && \
+ apt-get -y install curl git && \
+ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash && \
+ export NVM_DIR="$HOME/.nvm" && \
+ [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && \
+ nvm install v16.16.0 && \
+ nvm use v16.16.0
+
+RUN git clone https://github.com/uniquenetwork/polkadot-launch -b {{ POLKADOT_LAUNCH_BRANCH }}
+
+RUN export NVM_DIR="$HOME/.nvm" && \
+ [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && \
+ cd /polkadot-launch && \
+ npm install --global yarn && \
+ yarn install
+
+COPY --from=builder-unique /unique_parachain/unique-chain/.docker/testnet-config/launch-config.json /polkadot-launch/launch-config.json
+COPY --from=builder-unique /unique_parachain/unique-chain/target/release/unique-collator /unique-chain/target/release/
+
+COPY --from=uniquenetwork/builder-polkadot:{{ POLKADOT_BUILD_BRANCH }} /unique_parachain/polkadot/target/release/polkadot /polkadot/target/release/
+
+EXPOSE 9844
+EXPOSE 9944
+EXPOSE 9933
+EXPOSE 9833
+EXPOSE 40333
+EXPOSE 30333
+
+CMD export NVM_DIR="$HOME/.nvm" && \
+ [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && \
+ cd /polkadot-launch && \
+ yarn start launch-config.json
+
+
\ No newline at end of file
.docker/testnet-config/launch-config.jsondiffbeforeafterboth--- /dev/null
+++ b/.docker/testnet-config/launch-config.json
@@ -0,0 +1,121 @@
+{
+ "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",
+ "--rpc-cors=all",
+ "--unsafe-rpc-external",
+ "--unsafe-ws-external"
+ ]
+ },
+ {
+ "name": "bob",
+ "wsPort": 9855,
+ "rpcPort": 9854,
+ "port": 30555,
+ "flags": [
+ "-lparachain::candidate_validation=debug",
+ "-lxcm=trace",
+ "--rpc-cors=all",
+ "--unsafe-rpc-external",
+ "--unsafe-ws-external"
+ ]
+ },
+ {
+ "name": "charlie",
+ "wsPort": 9866,
+ "rpcPort": 9865,
+ "port": 30666,
+ "flags": [
+ "-lparachain::candidate_validation=debug",
+ "-lxcm=trace",
+ "--rpc-cors=all",
+ "--unsafe-rpc-external",
+ "--unsafe-ws-external"
+ ]
+ },
+ {
+ "name": "dave",
+ "wsPort": 9877,
+ "rpcPort": 9876,
+ "port": 30777,
+ "flags": [
+ "-lparachain::candidate_validation=debug",
+ "-lxcm=trace",
+ "--rpc-cors=all",
+ "--unsafe-rpc-external",
+ "--unsafe-ws-external"
+ ]
+ },
+ {
+ "name": "eve",
+ "wsPort": 9888,
+ "rpcPort": 9887,
+ "port": 30888,
+ "flags": [
+ "-lparachain::candidate_validation=debug",
+ "-lxcm=trace",
+ "--rpc-cors=all",
+ "--unsafe-rpc-external",
+ "--unsafe-ws-external"
+ ]
+ }
+ ],
+ "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"
+ ]
+ }
+ ]
+ }
+ ],
+ "simpleParachains": [],
+ "hrmpChannels": [],
+ "finalization": false
+}
.github/workflows/ci-develop.ymldiffbeforeafterboth--- a/.github/workflows/ci-develop.yml
+++ b/.github/workflows/ci-develop.yml
@@ -3,7 +3,7 @@
on:
pull_request:
branches: [ 'develop' ]
- types: [ opened, reopened, synchronize, ready_for_review ]
+ types: [ opened, reopened, synchronize, ready_for_review, converted_to_draft ]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref }}
@@ -12,23 +12,28 @@
jobs:
yarn-test-dev:
+ if: github.event.pull_request.draft == false
uses: ./.github/workflows/dev-build-tests_v2.yml
+
unit-test:
+ if: github.event.pull_request.draft == false
uses: ./.github/workflows/unit-test_v2.yml
canary:
- if: ${{ contains( github.event.pull_request.labels.*.name, 'canary') }}
+ if: ${{ (github.event.pull_request.draft == false && contains( github.event.pull_request.labels.*.name, 'canary')) }}
uses: ./.github/workflows/canary.yml
secrets: inherit # pass all secrets
xcm:
- if: ${{ contains( github.event.pull_request.labels.*.name, 'xcm') }}
+ if: ${{ (github.event.pull_request.draft == false && contains( github.event.pull_request.labels.*.name, 'xcm')) }}
uses: ./.github/workflows/xcm.yml
secrets: inherit # pass all secrets
codestyle:
+ if: github.event.pull_request.draft == false
uses: ./.github/workflows/codestyle_v2.yml
yarn_eslint:
+ if: github.event.pull_request.draft == false
uses: ./.github/workflows/test_codestyle_v2.yml
.github/workflows/testnet-build.ymldiffbeforeafterboth--- /dev/null
+++ b/.github/workflows/testnet-build.yml
@@ -0,0 +1,144 @@
+name: testnet-build
+
+# Controls when the action will run.
+on:
+ # Triggers the workflow on push or pull request events but only for the master branch
+ pull_request:
+ branches:
+ - master
+ types:
+ - opened
+ - reopened
+ - synchronize #commit(s) pushed to the pull request
+ - ready_for_review
+
+ # Allows you to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+#Define Workflow variables
+env:
+ REPO_URL: ${{ github.server_url }}/${{ github.repository }}
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+# A workflow run is made up of one or more jobs that can run sequentially or in parallel
+jobs:
+
+ prepare-execution-marix:
+
+ name: Prepare execution matrix
+
+ runs-on: [self-hosted-ci,medium]
+ outputs:
+ matrix: ${{ steps.create_matrix.outputs.matrix }}
+
+ steps:
+
+ - name: Clean Workspace
+ uses: AutoModality/action-clean@v1.1.0
+
+ # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
+ - uses: actions/checkout@v3
+ with:
+ ref: ${{ github.head_ref }} #Checking out head commit
+
+ - name: Read .env file
+ uses: xom9ikk/dotenv@v1.0.2
+
+ - name: Create Execution matrix
+ uses: fabiocaccamo/create-matrix-action@v2
+ id: create_matrix
+ with:
+ matrix: |
+ network {opal}, runtime {opal}, features {opal-runtime}
+ network {quartz}, runtime {quartz}, features {quartz-runtime}
+ network {unique}, runtime {unique}, features {unique-runtime}
+
+ testnet-build:
+ needs: prepare-execution-marix
+ # The type of runner that the job will run on
+ runs-on: [self-hosted-ci,medium]
+
+ timeout-minutes: 600
+
+ name: ${{ matrix.network }}
+
+ continue-on-error: true #Do not stop testing of matrix runs failed. As it decided during PR review - it required 50/50& Let's check it with false.
+
+ strategy:
+ matrix:
+ include: ${{fromJson(needs.prepare-execution-marix.outputs.matrix)}}
+
+ steps:
+ - name: Skip if pull request is in Draft
+ if: github.event.pull_request.draft == true
+ run: exit 1
+
+ - name: Clean Workspace
+ uses: AutoModality/action-clean@v1.1.0
+
+ # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
+ - uses: actions/checkout@v3
+ with:
+ ref: ${{ github.head_ref }} #Checking out head commit
+
+ - name: Read .env file
+ uses: xom9ikk/dotenv@v1.0.2
+
+ - name: Generate ENV related extend file for docker-compose
+ uses: cuchi/jinja2-action@v1.2.0
+ with:
+ template: .docker/Dockerfile-testnet.j2
+ output_file: .docker/Dockerfile-testnet.${{ matrix.network }}.yml
+ variables: |
+ RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }}
+ NETWORK=${{ matrix.network }}
+ POLKADOT_BUILD_BRANCH=${{ env.POLKADOT_BUILD_BRANCH }}
+ POLKADOT_LAUNCH_BRANCH=${{ env.POLKADOT_LAUNCH_BRANCH }}
+ FEATURE=${{ matrix.features }}
+ RUNTIME=${{ matrix.runtime }}
+ BRANCH=${{ github.head_ref }}
+
+ - name: Show build configuration
+ run: cat .docker/Dockerfile-testnet.${{ matrix.network }}.yml
+
+ - name: Show launch-config configuration
+ run: cat launch-config.json
+
+ - name: Run find-and-replace to remove slashes from branch name
+ uses: mad9000/actions-find-and-replace-string@2
+ id: branchname
+ with:
+ source: ${{ github.head_ref }}
+ find: '/'
+ replace: '-'
+
+ - name: Log in to Docker Hub
+ uses: docker/login-action@v2.0.0
+ with:
+ username: ${{ secrets.CORE_DOCKERHUB_USERNAME }}
+ password: ${{ secrets.CORE_DOCKERHUB_TOKEN }}
+
+ - name: Pull polkadot docker image
+ run: docker pull uniquenetwork/builder-polkadot:${{ env.POLKADOT_BUILD_BRANCH }}
+
+ - name: Build the stack
+ run: cd .docker/ && docker build --file ./Dockerfile-testnet.${{ matrix.network }}.yml --tag uniquenetwork/${{ matrix.network }}-testnet-local:nightly-${{ steps.branchname.outputs.value }}-${{ github.sha }} --tag uniquenetwork/${{ matrix.network }}-testnet-local:latest .
+
+ - name: Push docker version image
+ run: docker push uniquenetwork/${{ matrix.network }}-testnet-local:nightly-${{ steps.branchname.outputs.value }}-${{ github.sha }}
+
+ - name: Push docker latest image
+ run: docker push uniquenetwork/${{ matrix.network }}-testnet-local:latest
+
+ - name: Clean Workspace
+ if: always()
+ uses: AutoModality/action-clean@v1.1.0
+
+ - name: Remove builder cache
+ if: always() # run this step always
+ run: |
+ docker builder prune -f
+ docker system prune -f
pallets/common/src/erc.rsdiffbeforeafterboth--- a/pallets/common/src/erc.rs
+++ b/pallets/common/src/erc.rs
@@ -139,25 +139,26 @@
save(self)
}
- /// Set the substrate sponsor of the collection.
- ///
- /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
- ///
- /// @param sponsor Substrate address of the sponsor from whose account funds will be debited for operations with the contract.
- fn set_collection_sponsor_substrate(
- &mut self,
- caller: caller,
- sponsor: uint256,
- ) -> Result<void> {
- self.consume_store_reads_and_writes(1, 1)?;
+ // TODO: Temprorary off. Need refactor
+ // /// Set the substrate sponsor of the collection.
+ // ///
+ // /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
+ // ///
+ // /// @param sponsor Substrate address of the sponsor from whose account funds will be debited for operations with the contract.
+ // fn set_collection_sponsor_substrate(
+ // &mut self,
+ // caller: caller,
+ // sponsor: uint256,
+ // ) -> Result<void> {
+ // self.consume_store_reads_and_writes(1, 1)?;
- check_is_owner_or_admin(caller, self)?;
+ // check_is_owner_or_admin(caller, self)?;
- let sponsor = convert_uint256_to_cross_account::<T>(sponsor);
- self.set_sponsor(sponsor.as_sub().clone())
- .map_err(dispatch_to_evm::<T>)?;
- save(self)
- }
+ // let sponsor = convert_uint256_to_cross_account::<T>(sponsor);
+ // self.set_sponsor(sponsor.as_sub().clone())
+ // .map_err(dispatch_to_evm::<T>)?;
+ // save(self)
+ // }
/// Whether there is a pending sponsor.
fn has_collection_pending_sponsor(&self) -> Result<bool> {
@@ -299,35 +300,37 @@
Ok(crate::eth::collection_id_to_address(self.id))
}
- /// Add collection admin by substrate address.
- /// @param newAdmin Substrate administrator address.
- fn add_collection_admin_substrate(
- &mut self,
- caller: caller,
- new_admin: uint256,
- ) -> Result<void> {
- self.consume_store_writes(2)?;
+ // TODO: Temprorary off. Need refactor
+ // /// Add collection admin by substrate address.
+ // /// @param newAdmin Substrate administrator address.
+ // fn add_collection_admin_substrate(
+ // &mut self,
+ // caller: caller,
+ // new_admin: uint256,
+ // ) -> Result<void> {
+ // self.consume_store_writes(2)?;
- let caller = T::CrossAccountId::from_eth(caller);
- let new_admin = convert_uint256_to_cross_account::<T>(new_admin);
- <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;
- Ok(())
- }
+ // let caller = T::CrossAccountId::from_eth(caller);
+ // let new_admin = convert_uint256_to_cross_account::<T>(new_admin);
+ // <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;
+ // Ok(())
+ // }
- /// Remove collection admin by substrate address.
- /// @param admin Substrate administrator address.
- fn remove_collection_admin_substrate(
- &mut self,
- caller: caller,
- admin: uint256,
- ) -> Result<void> {
- self.consume_store_writes(2)?;
+ // TODO: Temprorary off. Need refactor
+ // /// Remove collection admin by substrate address.
+ // /// @param admin Substrate administrator address.
+ // fn remove_collection_admin_substrate(
+ // &mut self,
+ // caller: caller,
+ // admin: uint256,
+ // ) -> Result<void> {
+ // self.consume_store_writes(2)?;
- let caller = T::CrossAccountId::from_eth(caller);
- let admin = convert_uint256_to_cross_account::<T>(admin);
- <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;
- Ok(())
- }
+ // let caller = T::CrossAccountId::from_eth(caller);
+ // let admin = convert_uint256_to_cross_account::<T>(admin);
+ // <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;
+ // Ok(())
+ // }
/// Add collection admin.
/// @param newAdmin Address of the added administrator.
@@ -476,21 +479,22 @@
Ok(())
}
- /// Add substrate user to allowed list.
- ///
- /// @param user User substrate address.
- fn add_to_collection_allow_list_substrate(
- &mut self,
- caller: caller,
- user: uint256,
- ) -> Result<void> {
- self.consume_store_writes(1)?;
+ // TODO: Temprorary off. Need refactor
+ // /// Add substrate user to allowed list.
+ // ///
+ // /// @param user User substrate address.
+ // fn add_to_collection_allow_list_substrate(
+ // &mut self,
+ // caller: caller,
+ // user: uint256,
+ // ) -> Result<void> {
+ // self.consume_store_writes(1)?;
- let caller = T::CrossAccountId::from_eth(caller);
- let user = convert_uint256_to_cross_account::<T>(user);
- Pallet::<T>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;
- Ok(())
- }
+ // let caller = T::CrossAccountId::from_eth(caller);
+ // let user = convert_uint256_to_cross_account::<T>(user);
+ // Pallet::<T>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;
+ // Ok(())
+ // }
/// Remove the user from the allowed list.
///
@@ -504,21 +508,22 @@
Ok(())
}
- /// Remove substrate user from allowed list.
- ///
- /// @param user User substrate address.
- fn remove_from_collection_allow_list_substrate(
- &mut self,
- caller: caller,
- user: uint256,
- ) -> Result<void> {
- self.consume_store_writes(1)?;
+ // TODO: Temprorary off. Need refactor
+ // /// Remove substrate user from allowed list.
+ // ///
+ // /// @param user User substrate address.
+ // fn remove_from_collection_allow_list_substrate(
+ // &mut self,
+ // caller: caller,
+ // user: uint256,
+ // ) -> Result<void> {
+ // self.consume_store_writes(1)?;
- let caller = T::CrossAccountId::from_eth(caller);
- let user = convert_uint256_to_cross_account::<T>(user);
- Pallet::<T>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;
- Ok(())
- }
+ // let caller = T::CrossAccountId::from_eth(caller);
+ // let user = convert_uint256_to_cross_account::<T>(user);
+ // Pallet::<T>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;
+ // Ok(())
+ // }
/// Switch permission for minting.
///
@@ -551,14 +556,15 @@
Ok(self.is_owner_or_admin(&user))
}
- /// Check that substrate account is the owner or admin of the collection
- ///
- /// @param user account to verify
- /// @return "true" if account is the owner or admin
- fn is_owner_or_admin_substrate(&self, user: uint256) -> Result<bool> {
- let user = convert_uint256_to_cross_account::<T>(user);
- Ok(self.is_owner_or_admin(&user))
- }
+ // TODO: Temprorary off. Need refactor
+ // /// Check that substrate account is the owner or admin of the collection
+ // ///
+ // /// @param user account to verify
+ // /// @return "true" if account is the owner or admin
+ // fn is_owner_or_admin_substrate(&self, user: uint256) -> Result<bool> {
+ // let user = convert_uint256_to_cross_account::<T>(user);
+ // Ok(self.is_owner_or_admin(&user))
+ // }
/// Returns collection type
///
@@ -595,18 +601,19 @@
.map_err(dispatch_to_evm::<T>)
}
- /// Changes collection owner to another substrate account
- ///
- /// @dev Owner can be changed only by current owner
- /// @param newOwner new owner substrate account
- fn set_owner_substrate(&mut self, caller: caller, new_owner: uint256) -> Result<void> {
- self.consume_store_writes(1)?;
+ // TODO: Temprorary off. Need refactor
+ // /// Changes collection owner to another substrate account
+ // ///
+ // /// @dev Owner can be changed only by current owner
+ // /// @param newOwner new owner substrate account
+ // fn set_owner_substrate(&mut self, caller: caller, new_owner: uint256) -> Result<void> {
+ // self.consume_store_writes(1)?;
- let caller = T::CrossAccountId::from_eth(caller);
- let new_owner = convert_uint256_to_cross_account::<T>(new_owner);
- self.set_owner_internal(caller, new_owner)
- .map_err(dispatch_to_evm::<T>)
- }
+ // let caller = T::CrossAccountId::from_eth(caller);
+ // let new_owner = convert_uint256_to_cross_account::<T>(new_owner);
+ // self.set_owner_internal(caller, new_owner)
+ // .map_err(dispatch_to_evm::<T>)
+ // }
// TODO: need implement AbiWriter for &Vec<T>
// fn collection_admins(&self) -> Result<Vec<(address, uint256)>> {
pallets/fungible/src/stubs/UniqueFungible.rawdiffbeforeafterbothbinary blob — no preview
pallets/fungible/src/stubs/UniqueFungible.soldiffbeforeafterboth--- a/pallets/fungible/src/stubs/UniqueFungible.sol
+++ b/pallets/fungible/src/stubs/UniqueFungible.sol
@@ -18,7 +18,7 @@
}
/// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0x47dbc105
+/// @dev the ERC-165 identifier for this interface is 0x3e1e8083
contract Collection is Dummy, ERC165 {
/// Set collection property.
///
@@ -72,19 +72,6 @@
dummy = 0;
}
- /// Set the substrate sponsor of the collection.
- ///
- /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
- ///
- /// @param sponsor Substrate address of the sponsor from whose account funds will be debited for operations with the contract.
- /// @dev EVM selector for this function is: 0xc74d6751,
- /// or in textual repr: setCollectionSponsorSubstrate(uint256)
- function setCollectionSponsorSubstrate(uint256 sponsor) public {
- require(false, stub_error);
- sponsor;
- dummy = 0;
- }
-
/// Whether there is a pending sponsor.
/// @dev EVM selector for this function is: 0x058ac185,
/// or in textual repr: hasCollectionPendingSponsor()
@@ -167,26 +154,6 @@
return 0x0000000000000000000000000000000000000000;
}
- /// Add collection admin by substrate address.
- /// @param newAdmin Substrate administrator address.
- /// @dev EVM selector for this function is: 0x5730062b,
- /// or in textual repr: addCollectionAdminSubstrate(uint256)
- function addCollectionAdminSubstrate(uint256 newAdmin) public {
- require(false, stub_error);
- newAdmin;
- dummy = 0;
- }
-
- /// Remove collection admin by substrate address.
- /// @param admin Substrate administrator address.
- /// @dev EVM selector for this function is: 0x4048fcf9,
- /// or in textual repr: removeCollectionAdminSubstrate(uint256)
- function removeCollectionAdminSubstrate(uint256 admin) public {
- require(false, stub_error);
- admin;
- dummy = 0;
- }
-
/// Add collection admin.
/// @param newAdmin Address of the added administrator.
/// @dev EVM selector for this function is: 0x92e462c7,
@@ -262,17 +229,6 @@
/// @dev EVM selector for this function is: 0x67844fe6,
/// or in textual repr: addToCollectionAllowList(address)
function addToCollectionAllowList(address user) public {
- require(false, stub_error);
- user;
- dummy = 0;
- }
-
- /// Add substrate user to allowed list.
- ///
- /// @param user User substrate address.
- /// @dev EVM selector for this function is: 0xd06ad267,
- /// or in textual repr: addToCollectionAllowListSubstrate(uint256)
- function addToCollectionAllowListSubstrate(uint256 user) public {
require(false, stub_error);
user;
dummy = 0;
@@ -289,17 +245,6 @@
dummy = 0;
}
- /// Remove substrate user from allowed list.
- ///
- /// @param user User substrate address.
- /// @dev EVM selector for this function is: 0xa31913ed,
- /// or in textual repr: removeFromCollectionAllowListSubstrate(uint256)
- function removeFromCollectionAllowListSubstrate(uint256 user) public {
- require(false, stub_error);
- user;
- dummy = 0;
- }
-
/// Switch permission for minting.
///
/// @param mode Enable if "true".
@@ -324,19 +269,6 @@
return false;
}
- /// Check that substrate account is the owner or admin of the collection
- ///
- /// @param user account to verify
- /// @return "true" if account is the owner or admin
- /// @dev EVM selector for this function is: 0x68910e00,
- /// or in textual repr: isOwnerOrAdminSubstrate(uint256)
- function isOwnerOrAdminSubstrate(uint256 user) public view returns (bool) {
- require(false, stub_error);
- user;
- dummy;
- return false;
- }
-
/// Returns collection type
///
/// @return `Fungible` or `NFT` or `ReFungible`
@@ -367,18 +299,6 @@
/// @dev EVM selector for this function is: 0x13af4035,
/// or in textual repr: setOwner(address)
function setOwner(address newOwner) public {
- require(false, stub_error);
- newOwner;
- dummy = 0;
- }
-
- /// Changes collection owner to another substrate account
- ///
- /// @dev Owner can be changed only by current owner
- /// @param newOwner new owner substrate account
- /// @dev EVM selector for this function is: 0xb212138f,
- /// or in textual repr: setOwnerSubstrate(uint256)
- function setOwnerSubstrate(uint256 newOwner) public {
require(false, stub_error);
newOwner;
dummy = 0;
pallets/nonfungible/src/stubs/UniqueNFT.rawdiffbeforeafterbothbinary blob — no preview
pallets/nonfungible/src/stubs/UniqueNFT.soldiffbeforeafterboth--- a/pallets/nonfungible/src/stubs/UniqueNFT.sol
+++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol
@@ -91,7 +91,7 @@
}
/// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0x47dbc105
+/// @dev the ERC-165 identifier for this interface is 0x3e1e8083
contract Collection is Dummy, ERC165 {
/// Set collection property.
///
@@ -145,19 +145,6 @@
dummy = 0;
}
- /// Set the substrate sponsor of the collection.
- ///
- /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
- ///
- /// @param sponsor Substrate address of the sponsor from whose account funds will be debited for operations with the contract.
- /// @dev EVM selector for this function is: 0xc74d6751,
- /// or in textual repr: setCollectionSponsorSubstrate(uint256)
- function setCollectionSponsorSubstrate(uint256 sponsor) public {
- require(false, stub_error);
- sponsor;
- dummy = 0;
- }
-
/// Whether there is a pending sponsor.
/// @dev EVM selector for this function is: 0x058ac185,
/// or in textual repr: hasCollectionPendingSponsor()
@@ -240,26 +227,6 @@
return 0x0000000000000000000000000000000000000000;
}
- /// Add collection admin by substrate address.
- /// @param newAdmin Substrate administrator address.
- /// @dev EVM selector for this function is: 0x5730062b,
- /// or in textual repr: addCollectionAdminSubstrate(uint256)
- function addCollectionAdminSubstrate(uint256 newAdmin) public {
- require(false, stub_error);
- newAdmin;
- dummy = 0;
- }
-
- /// Remove collection admin by substrate address.
- /// @param admin Substrate administrator address.
- /// @dev EVM selector for this function is: 0x4048fcf9,
- /// or in textual repr: removeCollectionAdminSubstrate(uint256)
- function removeCollectionAdminSubstrate(uint256 admin) public {
- require(false, stub_error);
- admin;
- dummy = 0;
- }
-
/// Add collection admin.
/// @param newAdmin Address of the added administrator.
/// @dev EVM selector for this function is: 0x92e462c7,
@@ -335,17 +302,6 @@
/// @dev EVM selector for this function is: 0x67844fe6,
/// or in textual repr: addToCollectionAllowList(address)
function addToCollectionAllowList(address user) public {
- require(false, stub_error);
- user;
- dummy = 0;
- }
-
- /// Add substrate user to allowed list.
- ///
- /// @param user User substrate address.
- /// @dev EVM selector for this function is: 0xd06ad267,
- /// or in textual repr: addToCollectionAllowListSubstrate(uint256)
- function addToCollectionAllowListSubstrate(uint256 user) public {
require(false, stub_error);
user;
dummy = 0;
@@ -362,17 +318,6 @@
dummy = 0;
}
- /// Remove substrate user from allowed list.
- ///
- /// @param user User substrate address.
- /// @dev EVM selector for this function is: 0xa31913ed,
- /// or in textual repr: removeFromCollectionAllowListSubstrate(uint256)
- function removeFromCollectionAllowListSubstrate(uint256 user) public {
- require(false, stub_error);
- user;
- dummy = 0;
- }
-
/// Switch permission for minting.
///
/// @param mode Enable if "true".
@@ -397,19 +342,6 @@
return false;
}
- /// Check that substrate account is the owner or admin of the collection
- ///
- /// @param user account to verify
- /// @return "true" if account is the owner or admin
- /// @dev EVM selector for this function is: 0x68910e00,
- /// or in textual repr: isOwnerOrAdminSubstrate(uint256)
- function isOwnerOrAdminSubstrate(uint256 user) public view returns (bool) {
- require(false, stub_error);
- user;
- dummy;
- return false;
- }
-
/// Returns collection type
///
/// @return `Fungible` or `NFT` or `ReFungible`
@@ -440,18 +372,6 @@
/// @dev EVM selector for this function is: 0x13af4035,
/// or in textual repr: setOwner(address)
function setOwner(address newOwner) public {
- require(false, stub_error);
- newOwner;
- dummy = 0;
- }
-
- /// Changes collection owner to another substrate account
- ///
- /// @dev Owner can be changed only by current owner
- /// @param newOwner new owner substrate account
- /// @dev EVM selector for this function is: 0xb212138f,
- /// or in textual repr: setOwnerSubstrate(uint256)
- function setOwnerSubstrate(uint256 newOwner) public {
require(false, stub_error);
newOwner;
dummy = 0;
pallets/refungible/src/stubs/UniqueRefungible.rawdiffbeforeafterbothbinary blob — no preview
pallets/refungible/src/stubs/UniqueRefungible.soldiffbeforeafterboth--- a/pallets/refungible/src/stubs/UniqueRefungible.sol
+++ b/pallets/refungible/src/stubs/UniqueRefungible.sol
@@ -91,7 +91,7 @@
}
/// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0x47dbc105
+/// @dev the ERC-165 identifier for this interface is 0x3e1e8083
contract Collection is Dummy, ERC165 {
/// Set collection property.
///
@@ -145,19 +145,6 @@
dummy = 0;
}
- /// Set the substrate sponsor of the collection.
- ///
- /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
- ///
- /// @param sponsor Substrate address of the sponsor from whose account funds will be debited for operations with the contract.
- /// @dev EVM selector for this function is: 0xc74d6751,
- /// or in textual repr: setCollectionSponsorSubstrate(uint256)
- function setCollectionSponsorSubstrate(uint256 sponsor) public {
- require(false, stub_error);
- sponsor;
- dummy = 0;
- }
-
/// Whether there is a pending sponsor.
/// @dev EVM selector for this function is: 0x058ac185,
/// or in textual repr: hasCollectionPendingSponsor()
@@ -240,26 +227,6 @@
return 0x0000000000000000000000000000000000000000;
}
- /// Add collection admin by substrate address.
- /// @param newAdmin Substrate administrator address.
- /// @dev EVM selector for this function is: 0x5730062b,
- /// or in textual repr: addCollectionAdminSubstrate(uint256)
- function addCollectionAdminSubstrate(uint256 newAdmin) public {
- require(false, stub_error);
- newAdmin;
- dummy = 0;
- }
-
- /// Remove collection admin by substrate address.
- /// @param admin Substrate administrator address.
- /// @dev EVM selector for this function is: 0x4048fcf9,
- /// or in textual repr: removeCollectionAdminSubstrate(uint256)
- function removeCollectionAdminSubstrate(uint256 admin) public {
- require(false, stub_error);
- admin;
- dummy = 0;
- }
-
/// Add collection admin.
/// @param newAdmin Address of the added administrator.
/// @dev EVM selector for this function is: 0x92e462c7,
@@ -335,17 +302,6 @@
/// @dev EVM selector for this function is: 0x67844fe6,
/// or in textual repr: addToCollectionAllowList(address)
function addToCollectionAllowList(address user) public {
- require(false, stub_error);
- user;
- dummy = 0;
- }
-
- /// Add substrate user to allowed list.
- ///
- /// @param user User substrate address.
- /// @dev EVM selector for this function is: 0xd06ad267,
- /// or in textual repr: addToCollectionAllowListSubstrate(uint256)
- function addToCollectionAllowListSubstrate(uint256 user) public {
require(false, stub_error);
user;
dummy = 0;
@@ -362,17 +318,6 @@
dummy = 0;
}
- /// Remove substrate user from allowed list.
- ///
- /// @param user User substrate address.
- /// @dev EVM selector for this function is: 0xa31913ed,
- /// or in textual repr: removeFromCollectionAllowListSubstrate(uint256)
- function removeFromCollectionAllowListSubstrate(uint256 user) public {
- require(false, stub_error);
- user;
- dummy = 0;
- }
-
/// Switch permission for minting.
///
/// @param mode Enable if "true".
@@ -397,19 +342,6 @@
return false;
}
- /// Check that substrate account is the owner or admin of the collection
- ///
- /// @param user account to verify
- /// @return "true" if account is the owner or admin
- /// @dev EVM selector for this function is: 0x68910e00,
- /// or in textual repr: isOwnerOrAdminSubstrate(uint256)
- function isOwnerOrAdminSubstrate(uint256 user) public view returns (bool) {
- require(false, stub_error);
- user;
- dummy;
- return false;
- }
-
/// Returns collection type
///
/// @return `Fungible` or `NFT` or `ReFungible`
@@ -440,18 +372,6 @@
/// @dev EVM selector for this function is: 0x13af4035,
/// or in textual repr: setOwner(address)
function setOwner(address newOwner) public {
- require(false, stub_error);
- newOwner;
- dummy = 0;
- }
-
- /// Changes collection owner to another substrate account
- ///
- /// @dev Owner can be changed only by current owner
- /// @param newOwner new owner substrate account
- /// @dev EVM selector for this function is: 0xb212138f,
- /// or in textual repr: setOwnerSubstrate(uint256)
- function setOwnerSubstrate(uint256 newOwner) public {
require(false, stub_error);
newOwner;
dummy = 0;
tests/package.jsondiffbeforeafterboth--- a/tests/package.json
+++ b/tests/package.json
@@ -88,8 +88,11 @@
"testLimits": "mocha --timeout 9999999 -r ts-node/register ./**/limits.test.ts",
"testEthCreateNFTCollection": "mocha --timeout 9999999 -r ts-node/register ./**/eth/createNFTCollection.test.ts",
"testEthCreateRFTCollection": "mocha --timeout 9999999 -r ts-node/register ./**/eth/createRFTCollection.test.ts",
+ "testEthNFT": "mocha --timeout 9999999 -r ts-node/register ./**/eth/nonFungible.test.ts",
"testRFT": "mocha --timeout 9999999 -r ts-node/register ./**/refungible.test.ts",
+ "testEthRFT": "mocha --timeout 9999999 -r ts-node/register ./**/eth/reFungible.test.ts ./**/eth/reFungibleToken.test.ts",
"testFT": "mocha --timeout 9999999 -r ts-node/register ./**/fungible.test.ts",
+ "testEthFT": "mocha --timeout 9999999 -r ts-node/register ./**/eth/fungible.test.ts",
"testRPC": "mocha --timeout 9999999 -r ts-node/register ./**/rpc.test.ts",
"testPromotion": "mocha --timeout 9999999 -r ts-node/register ./**/app-promotion.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",
tests/src/change-collection-owner.test.tsdiffbeforeafterboth--- a/tests/src/change-collection-owner.test.ts
+++ b/tests/src/change-collection-owner.test.ts
@@ -31,11 +31,11 @@
itSub('Changing owner changes owner address', async ({helper}) => {
const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
const beforeChanging = await helper.collection.getData(collection.collectionId);
- expect(beforeChanging?.normalizedOwner).to.be.equal(helper.util.normalizeSubstrateAddress(alice.address));
+ expect(beforeChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(alice.address));
await collection.changeOwner(alice, bob.address);
const afterChanging = await helper.collection.getData(collection.collectionId);
- expect(afterChanging?.normalizedOwner).to.be.equal(helper.util.normalizeSubstrateAddress(bob.address));
+ expect(afterChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(bob.address));
});
});
@@ -60,7 +60,7 @@
await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/);
const afterChanging = await helper.collection.getData(collection.collectionId);
- expect(afterChanging?.normalizedOwner).to.be.equal(helper.util.normalizeSubstrateAddress(bob.address));
+ expect(afterChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(bob.address));
});
itSub('New collectionOwner has access to sponsorship management operations in the collection', async ({helper}) => {
@@ -68,7 +68,7 @@
await collection.changeOwner(alice, bob.address);
const afterChanging = await helper.collection.getData(collection.collectionId);
- expect(afterChanging?.normalizedOwner).to.be.equal(helper.util.normalizeSubstrateAddress(bob.address));
+ expect(afterChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(bob.address));
await collection.setSponsor(bob, charlie.address);
await collection.confirmSponsorship(charlie);
@@ -97,7 +97,7 @@
await collection.changeOwner(alice, bob.address);
await collection.changeOwner(bob, charlie.address);
const collectionData = await collection.getData();
- expect(collectionData?.normalizedOwner).to.be.equal(helper.util.normalizeSubstrateAddress(charlie.address));
+ expect(collectionData?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(charlie.address));
});
});
@@ -140,7 +140,7 @@
await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/);
const afterChanging = await helper.collection.getData(collection.collectionId);
- expect(afterChanging?.normalizedOwner).to.be.equal(helper.util.normalizeSubstrateAddress(bob.address));
+ expect(afterChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(bob.address));
const setSponsorTx = async () => collection.setSponsor(alice, charlie.address);
const confirmSponsorshipTx = async () => collection.confirmSponsorship(alice);
tests/src/createCollection.test.tsdiffbeforeafterboth--- a/tests/src/createCollection.test.ts
+++ b/tests/src/createCollection.test.ts
@@ -14,12 +14,12 @@
// You should have received a copy of the GNU General Public License
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+import {IKeyringPair} from '@polkadot/types/types';
import {usingPlaygrounds, expect, itSub, Pallets} from './util/playgrounds';
-import {IKeyringPair} from '@polkadot/types/types';
import {ICollectionCreationOptions, IProperty} from './util/playgrounds/types';
-import {DevUniqueHelper} from './util/playgrounds/unique.dev';
+import {UniqueHelper} from './util/playgrounds/unique';
-async function mintCollectionHelper(helper: DevUniqueHelper, signer: IKeyringPair, options: ICollectionCreationOptions, type?: 'nft' | 'fungible' | 'refungible') {
+async function mintCollectionHelper(helper: UniqueHelper, signer: IKeyringPair, options: ICollectionCreationOptions, type?: 'nft' | 'fungible' | 'refungible') {
let collection;
if (type === 'nft') {
collection = await helper.nft.mintCollection(signer, options);
@@ -29,7 +29,7 @@
collection = await helper.rft.mintCollection(signer, options);
}
const data = await collection.getData();
- expect(data?.normalizedOwner).to.be.equal(helper.util.normalizeSubstrateAddress(signer.address));
+ expect(data?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(signer.address));
expect(data?.name).to.be.equal(options.name);
expect(data?.description).to.be.equal(options.description);
expect(data?.raw.tokenPrefix).to.be.equal(options.tokenPrefix);
@@ -54,32 +54,27 @@
});
});
itSub('Create new NFT collection', async ({helper}) => {
-
await mintCollectionHelper(helper, alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 'nft');
});
itSub('Create new NFT collection whith collection_name of maximum length (64 bytes)', async ({helper}) => {
-
await mintCollectionHelper(helper, alice, {name: 'A'.repeat(64), description: 'descr', tokenPrefix: 'COL'}, 'nft');
});
itSub('Create new NFT collection whith collection_description of maximum length (256 bytes)', async ({helper}) => {
-
await mintCollectionHelper(helper, alice, {name: 'name', description: 'A'.repeat(256), tokenPrefix: 'COL'}, 'nft');
});
itSub('Create new NFT collection whith token_prefix of maximum length (16 bytes)', async ({helper}) => {
-
await mintCollectionHelper(helper, alice, {name: 'name', description: 'descr', tokenPrefix: 'A'.repeat(16)}, 'nft');
});
+
itSub('Create new Fungible collection', async ({helper}) => {
-
await mintCollectionHelper(helper, alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'}, 'fungible');
});
+
itSub.ifWithPallets('Create new ReFungible collection', [Pallets.ReFungible], async ({helper}) => {
-
await mintCollectionHelper(helper, alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'}, 'refungible');
});
itSub('create new collection with properties', async ({helper}) => {
-
await mintCollectionHelper(helper, alice, {
name: 'name', description: 'descr', tokenPrefix: 'COL',
properties: [{key: 'key1', value: 'val1'}],
@@ -88,7 +83,6 @@
});
itSub('Create new collection with extra fields', async ({helper}) => {
-
const collection = await mintCollectionHelper(helper, alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'}, 'fungible');
await collection.setPermissions(alice, {access: 'AllowList'});
await collection.setLimits(alice, {accountTokenOwnershipLimit: 3});
@@ -96,7 +90,7 @@
const limits = await collection.getEffectiveLimits();
const raw = data?.raw;
- expect(data?.normalizedOwner).to.be.equal(helper.util.normalizeSubstrateAddress(alice.address));
+ expect(data?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(alice.address));
expect(data?.name).to.be.equal('name');
expect(data?.description).to.be.equal('descr');
expect(raw.permissions.access).to.be.equal('AllowList');
@@ -105,7 +99,6 @@
});
itSub('New collection is not external', async ({helper}) => {
-
const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'});
const data = await collection.getData();
expect(data?.raw.readOnly).to.be.false;
@@ -131,12 +124,11 @@
await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error');
});
itSub('(!negative test!) create new NFT collection whith incorrect data (token_prefix)', async ({helper}) => {
-
const mintCollectionTx = async () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'A'.repeat(17)});
await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error');
});
+
itSub('(!negative test!) fails when bad limits are set', async ({helper}) => {
-
const mintCollectionTx = async () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', limits: {tokenLimit: 0}});
await expect(mintCollectionTx()).to.be.rejectedWith(/common\.CollectionTokenLimitExceeded/);
});
tests/src/createItem.test.tsdiffbeforeafterboth--- a/tests/src/createItem.test.ts
+++ b/tests/src/createItem.test.ts
@@ -15,24 +15,14 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import {IKeyringPair} from '@polkadot/types/types';
-
-import {
- createCollection,
- itApi,
- normalizeAccountId,
- getCreateItemResult,
- CrossAccountId,
-} from './util/helpers';
-
import {usingPlaygrounds, expect, itSub, Pallets} from './util/playgrounds';
-import {IProperty} from './util/playgrounds/types';
-import {executeTransaction} from './substrate/substrate-api';
-import {DevUniqueHelper} from './util/playgrounds/unique.dev';
+import {IProperty, ICrossAccountId} from './util/playgrounds/types';
+import {UniqueHelper} from './util/playgrounds/unique';
-async function mintTokenHelper(helper: DevUniqueHelper, collection: any, signer: IKeyringPair, owner: CrossAccountId, type: 'nft' | 'fungible' | 'refungible'='nft', properties?: IProperty[]) {
+async function mintTokenHelper(helper: UniqueHelper, collection: any, signer: IKeyringPair, owner: ICrossAccountId, type: 'nft' | 'fungible' | 'refungible'='nft', properties?: IProperty[]) {
let token;
const itemCountBefore = await helper.collection.getLastTokenId(collection.collectionId);
- const itemBalanceBefore = (await helper.api!.rpc.unique.balance(collection.collectionId, owner, 0)).toBigInt();
+ const itemBalanceBefore = (await helper.callRpc('api.rpc.unique.balance', [collection.collectionId, owner, 0])).toBigInt();
if (type === 'nft') {
token = await collection.mintToken(signer, owner, properties);
} else if (type === 'fungible') {
@@ -42,7 +32,7 @@
}
const itemCountAfter = await helper.collection.getLastTokenId(collection.collectionId);
- const itemBalanceAfter = (await helper.api!.rpc.unique.balance(collection.collectionId, owner, 0)).toBigInt();
+ const itemBalanceAfter = (await helper.callRpc('api.rpc.unique.balance', [collection.collectionId, owner, 0])).toBigInt();
if (type === 'fungible') {
expect(itemBalanceAfter - itemBalanceBefore).to.be.equal(10n);
@@ -75,27 +65,22 @@
});
itSub('Check events on create new item in Fungible collection', async ({helper}) => {
const {collectionId} = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0);
- const api = helper.api!;
-
-
- const to = normalizeAccountId(alice);
+ const to = {Substrate: alice.address};
{
const createData = {fungible: {value: 100}};
- const tx = api.tx.unique.createItem(collectionId, to, createData as any);
- const events = await executeTransaction(api, alice, tx);
- const result = getCreateItemResult(events);
- expect(result.amount).to.be.equal(100);
- expect(result.collectionId).to.be.equal(collectionId);
- expect(result.recipient).to.be.deep.equal(to);
+ const events = await helper.executeExtrinsic(alice, 'api.tx.unique.createItem', [collectionId, to, createData as any]);
+ const result = helper.util.extractTokensFromCreationResult(events);
+ expect(result.tokens[0].amount).to.be.equal(100n);
+ expect(result.tokens[0].collectionId).to.be.equal(collectionId);
+ expect(result.tokens[0].owner).to.be.deep.equal(to);
}
{
const createData = {fungible: {value: 50}};
- const tx = api.tx.unique.createItem(collectionId, to, createData as any);
- const events = await executeTransaction(api, alice, tx);
- const result = getCreateItemResult(events);
- expect(result.amount).to.be.equal(50);
- expect(result.collectionId).to.be.equal(collectionId);
- expect(result.recipient).to.be.deep.equal(to);
+ const events = await helper.executeExtrinsic(alice, 'api.tx.unique.createItem', [collectionId, to, createData as any]);
+ const result = helper.util.extractTokensFromCreationResult(events);
+ expect(result.tokens[0].amount).to.be.equal(50n);
+ expect(result.tokens[0].collectionId).to.be.equal(collectionId);
+ expect(result.tokens[0].owner).to.be.deep.equal(to);
}
});
itSub.ifWithPallets('Create new item in ReFungible collection', [Pallets.ReFungible], async ({helper}) => {
@@ -162,12 +147,12 @@
const amount = 1n;
const token = await mintTokenHelper(helper, collection, alice, {Substrate: bob.address});
{
- const totalPieces = await helper.api?.rpc.unique.totalPieces(collection.collectionId, token.tokenId);
+ const totalPieces = await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, token.tokenId]);
expect(totalPieces?.unwrap().toBigInt()).to.be.equal(amount);
}
await token.transfer(bob, {Substrate: alice.address});
{
- const totalPieces = await helper.api?.rpc.unique.totalPieces(collection.collectionId, token.tokenId);
+ const totalPieces = await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, token.tokenId]);
expect(totalPieces?.unwrap().toBigInt()).to.be.equal(amount);
}
});
@@ -267,21 +252,21 @@
itSub('Check total pieces for invalid Fungible token', async ({helper}) => {
const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
const invalidTokenId = 1_000_000;
- expect((await helper.api?.rpc.unique.totalPieces(collection.collectionId, invalidTokenId))?.isNone).to.be.true;
- expect((await helper.api?.rpc.unique.tokenData(collection.collectionId, invalidTokenId))?.pieces.toBigInt()).to.be.equal(0n);
+ expect((await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, invalidTokenId]))?.isNone).to.be.true;
+ expect((await helper.callRpc('api.rpc.unique.tokenData', [collection.collectionId, invalidTokenId]))?.pieces.toBigInt()).to.be.equal(0n);
});
itSub('Check total pieces for invalid NFT token', async ({helper}) => {
const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
const invalidTokenId = 1_000_000;
- expect((await helper.api?.rpc.unique.totalPieces(collection.collectionId, invalidTokenId))?.isNone).to.be.true;
- expect((await helper.api?.rpc.unique.tokenData(collection.collectionId, invalidTokenId))?.pieces.toBigInt()).to.be.equal(0n);
+ expect((await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, invalidTokenId]))?.isNone).to.be.true;
+ expect((await helper.callRpc('api.rpc.unique.tokenData', [collection.collectionId, invalidTokenId]))?.pieces.toBigInt()).to.be.equal(0n);
});
itSub.ifWithPallets('Check total pieces for invalid Refungible token', [Pallets.ReFungible], async ({helper}) => {
const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
const invalidTokenId = 1_000_000;
- expect((await helper.api?.rpc.unique.totalPieces(collection.collectionId, invalidTokenId))?.isNone).to.be.true;
- expect((await helper.api?.rpc.unique.tokenData(collection.collectionId, invalidTokenId))?.pieces.toBigInt()).to.be.equal(0n);
+ expect((await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, invalidTokenId]))?.isNone).to.be.true;
+ expect((await helper.callRpc('api.rpc.unique.tokenData', [collection.collectionId, invalidTokenId]))?.pieces.toBigInt()).to.be.equal(0n);
});
});
tests/src/createMultipleItems.test.tsdiffbeforeafterboth--- a/tests/src/createMultipleItems.test.ts
+++ b/tests/src/createMultipleItems.test.ts
@@ -15,12 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import {IKeyringPair} from '@polkadot/types/types';
-import {
- normalizeAccountId,
-} from './util/helpers';
import {usingPlaygrounds, expect, Pallets, itSub} from './util/playgrounds';
-
describe('Integration Test createMultipleItems(collection_id, owner, items_data):', () => {
let alice: IKeyringPair;
@@ -48,7 +44,7 @@
const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
for (const [i, token] of tokens.entries()) {
const tokenData = await token.getData();
- expect(tokenData?.normalizedOwner).to.be.deep.equal({Substrate: helper.util.normalizeSubstrateAddress(alice.address)});
+ expect(tokenData?.normalizedOwner.Substrate).to.be.deep.equal(helper.address.normalizeSubstrate(alice.address));
expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value);
}
});
@@ -116,7 +112,7 @@
const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
for (const [i, token] of tokens.entries()) {
const tokenData = await token.getData();
- expect(tokenData?.normalizedOwner).to.be.deep.equal({Substrate: helper.util.normalizeSubstrateAddress(alice.address)});
+ expect(tokenData?.normalizedOwner.Substrate).to.be.deep.equal(helper.address.normalizeSubstrate(alice.address));
expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value);
}
});
@@ -138,7 +134,7 @@
const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
for (const [i, token] of tokens.entries()) {
const tokenData = await token.getData();
- expect(tokenData?.normalizedOwner).to.be.deep.equal({Substrate: helper.util.normalizeSubstrateAddress(alice.address)});
+ expect(tokenData?.normalizedOwner.Substrate).to.be.deep.equal(helper.address.normalizeSubstrate(alice.address));
expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value);
}
});
@@ -160,7 +156,7 @@
const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
for (const [i, token] of tokens.entries()) {
const tokenData = await token.getData();
- expect(tokenData?.normalizedOwner).to.be.deep.equal({Substrate: helper.util.normalizeSubstrateAddress(alice.address)});
+ expect(tokenData?.normalizedOwner.Substrate).to.be.equal(helper.address.normalizeSubstrate(alice.address));
expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value);
}
});
@@ -275,8 +271,11 @@
});
const types = ['NFT', 'Fungible', 'ReFungible'];
- const mintTx = helper.api?.tx.unique.createMultipleItems(collectionId, normalizeAccountId(alice.address), types);
- await expect(helper.signTransaction(alice, mintTx)).to.be.rejected;
+ await expect(helper.executeExtrinsic(
+ alice,
+ 'api.tx.unique.createMultipleItems',
+ [collectionId, {Substrate: alice.address}, types],
+ )).to.be.rejectedWith(/nonfungible\.NotNonfungibleDataUsedToMintFungibleCollectionToken/);
});
itSub('Create tokens with different data limits <> maximum data limit', async ({helper}) => {
tests/src/creditFeesToTreasury.test.tsdiffbeforeafterboth--- a/tests/src/creditFeesToTreasury.test.ts
+++ b/tests/src/creditFeesToTreasury.test.ts
@@ -16,11 +16,6 @@
import './interfaces/augment-api-consts';
import {IKeyringPair} from '@polkadot/types/types';
-import {
- UNIQUE,
-} from './util/helpers';
-
-import {default as waitNewBlocks} from './substrate/wait-new-blocks';
import {ApiPromise} from '@polkadot/api';
import {usingPlaygrounds, expect, itSub} from './util/playgrounds';
@@ -63,7 +58,7 @@
itSub('Total issuance does not change', async ({helper}) => {
const api = helper.api!;
await skipInflationBlock(api);
- await waitNewBlocks(api, 1);
+ await helper.wait.newBlocks(1);
const totalBefore = (await api.query.balances.totalIssuance()).toBigInt();
@@ -75,9 +70,8 @@
});
itSub('Sender balance decreased by fee+sent amount, Treasury balance increased by fee', async ({helper}) => {
- const api = helper.api!;
- await skipInflationBlock(api);
- await waitNewBlocks(api, 1);
+ await skipInflationBlock(helper.api!);
+ await helper.wait.newBlocks(1);
const treasuryBalanceBefore = await helper.balance.getSubstrate(TREASURY);
const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
@@ -96,7 +90,7 @@
itSub('Treasury balance increased by failed tx fee', async ({helper}) => {
const api = helper.api!;
- await waitNewBlocks(api, 1);
+ await helper.wait.newBlocks(1);
const treasuryBalanceBefore = await helper.balance.getSubstrate(TREASURY);
const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
@@ -113,9 +107,8 @@
});
itSub('NFT Transactions also send fees to Treasury', async ({helper}) => {
- const api = helper.api!;
- await skipInflationBlock(api);
- await waitNewBlocks(api, 1);
+ await skipInflationBlock(helper.api!);
+ await helper.wait.newBlocks(1);
const treasuryBalanceBefore = await helper.balance.getSubstrate(TREASURY);
const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
@@ -131,9 +124,9 @@
});
itSub('Fees are sane', async ({helper}) => {
- const api = helper.api!;
- await skipInflationBlock(api);
- await waitNewBlocks(api, 1);
+ const unique = helper.balance.getOneTokenNominal();
+ await skipInflationBlock(helper.api!);
+ await helper.wait.newBlocks(1);
const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
@@ -142,14 +135,13 @@
const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);
const fee = aliceBalanceBefore - aliceBalanceAfter;
- expect(fee / UNIQUE < BigInt(Math.ceil(saneMaximumFee + createCollectionDeposit))).to.be.true;
- expect(fee / UNIQUE < BigInt(Math.ceil(saneMinimumFee + createCollectionDeposit))).to.be.true;
+ expect(fee / unique < BigInt(Math.ceil(saneMaximumFee + createCollectionDeposit))).to.be.true;
+ expect(fee / unique < BigInt(Math.ceil(saneMinimumFee + createCollectionDeposit))).to.be.true;
});
itSub('NFT Transfer fee is close to 0.1 Unique', async ({helper}) => {
- const api = helper.api!;
- await skipInflationBlock(api);
- await waitNewBlocks(api, 1);
+ await skipInflationBlock(helper.api!);
+ await helper.wait.newBlocks(1);
const collection = await helper.nft.mintCollection(alice, {
name: 'test',
@@ -163,7 +155,7 @@
await token.transfer(alice, {Substrate: bob.address});
const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);
- const fee = Number(aliceBalanceBefore - aliceBalanceAfter) / Number(UNIQUE);
+ const fee = Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal());
const expectedTransferFee = 0.1;
// fee drifts because of NextFeeMultiplier
const tolerance = 0.001;
tests/src/destroyCollection.test.tsdiffbeforeafterboth--- a/tests/src/destroyCollection.test.ts
+++ b/tests/src/destroyCollection.test.ts
@@ -15,10 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import {IKeyringPair} from '@polkadot/types/types';
-import {
- Pallets,
-} from './util/helpers';
-import {itSub, expect, usingPlaygrounds} from './util/playgrounds';
+import {itSub, expect, usingPlaygrounds, Pallets} from './util/playgrounds';
describe('integration test: ext. destroyCollection():', () => {
let alice: IKeyringPair;
tests/src/eth/allowlist.test.tsdiffbeforeafterboth--- a/tests/src/eth/allowlist.test.ts
+++ b/tests/src/eth/allowlist.test.ts
@@ -89,20 +89,21 @@
expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.false;
});
- itEth('Collection allowlist can be added and removed by [sub] address', async ({helper}) => {
- const owner = await helper.eth.createAccountWithBalance(donor);
- const user = donor;
+ // TODO: Temprorary off. Need refactor
+ // itEth('Collection allowlist can be added and removed by [sub] address', async ({helper}) => {
+ // const owner = await helper.eth.createAccountWithBalance(donor);
+ // const user = donor;
- const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
- const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+ // const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ // const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
- expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;
- await collectionEvm.methods.addToCollectionAllowListSubstrate(user.addressRaw).send({from: owner});
- expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.true;
+ // expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;
+ // await collectionEvm.methods.addToCollectionAllowListSubstrate(user.addressRaw).send({from: owner});
+ // expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.true;
- await collectionEvm.methods.removeFromCollectionAllowListSubstrate(user.addressRaw).send({from: owner});
- expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;
- });
+ // await collectionEvm.methods.removeFromCollectionAllowListSubstrate(user.addressRaw).send({from: owner});
+ // expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;
+ // });
itEth('Collection allowlist can not be add and remove [eth] address by not owner', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
@@ -122,21 +123,22 @@
expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.true;
});
- itEth('Collection allowlist can not be add and remove [sub] address by not owner', async ({helper}) => {
- const owner = await helper.eth.createAccountWithBalance(donor);
- const notOwner = await helper.eth.createAccountWithBalance(donor);
- const user = donor;
+ // TODO: Temprorary off. Need refactor
+ // itEth('Collection allowlist can not be add and remove [sub] address by not owner', async ({helper}) => {
+ // const owner = await helper.eth.createAccountWithBalance(donor);
+ // const notOwner = await helper.eth.createAccountWithBalance(donor);
+ // const user = donor;
- const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
- const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+ // const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+ // const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
- expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;
- await expect(collectionEvm.methods.addToCollectionAllowListSubstrate(user.addressRaw).call({from: notOwner})).to.be.rejectedWith('NoPermission');
- expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;
- await collectionEvm.methods.addToCollectionAllowListSubstrate(user.addressRaw).send({from: owner});
+ // expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;
+ // await expect(collectionEvm.methods.addToCollectionAllowListSubstrate(user.addressRaw).call({from: notOwner})).to.be.rejectedWith('NoPermission');
+ // expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;
+ // await collectionEvm.methods.addToCollectionAllowListSubstrate(user.addressRaw).send({from: owner});
- expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.true;
- await expect(collectionEvm.methods.removeFromCollectionAllowListSubstrate(user.addressRaw).call({from: notOwner})).to.be.rejectedWith('NoPermission');
- expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.true;
- });
+ // expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.true;
+ // await expect(collectionEvm.methods.removeFromCollectionAllowListSubstrate(user.addressRaw).call({from: notOwner})).to.be.rejectedWith('NoPermission');
+ // expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.true;
+ // });
});
tests/src/eth/api/UniqueFungible.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueFungible.sol
+++ b/tests/src/eth/api/UniqueFungible.sol
@@ -13,7 +13,7 @@
}
/// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0x47dbc105
+/// @dev the ERC-165 identifier for this interface is 0x3e1e8083
interface Collection is Dummy, ERC165 {
/// Set collection property.
///
@@ -48,15 +48,6 @@
/// @dev EVM selector for this function is: 0x7623402e,
/// or in textual repr: setCollectionSponsor(address)
function setCollectionSponsor(address sponsor) external;
-
- /// Set the substrate sponsor of the collection.
- ///
- /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
- ///
- /// @param sponsor Substrate address of the sponsor from whose account funds will be debited for operations with the contract.
- /// @dev EVM selector for this function is: 0xc74d6751,
- /// or in textual repr: setCollectionSponsorSubstrate(uint256)
- function setCollectionSponsorSubstrate(uint256 sponsor) external;
/// Whether there is a pending sponsor.
/// @dev EVM selector for this function is: 0x058ac185,
@@ -112,18 +103,6 @@
/// or in textual repr: contractAddress()
function contractAddress() external view returns (address);
- /// Add collection admin by substrate address.
- /// @param newAdmin Substrate administrator address.
- /// @dev EVM selector for this function is: 0x5730062b,
- /// or in textual repr: addCollectionAdminSubstrate(uint256)
- function addCollectionAdminSubstrate(uint256 newAdmin) external;
-
- /// Remove collection admin by substrate address.
- /// @param admin Substrate administrator address.
- /// @dev EVM selector for this function is: 0x4048fcf9,
- /// or in textual repr: removeCollectionAdminSubstrate(uint256)
- function removeCollectionAdminSubstrate(uint256 admin) external;
-
/// Add collection admin.
/// @param newAdmin Address of the added administrator.
/// @dev EVM selector for this function is: 0x92e462c7,
@@ -174,13 +153,6 @@
/// or in textual repr: addToCollectionAllowList(address)
function addToCollectionAllowList(address user) external;
- /// Add substrate user to allowed list.
- ///
- /// @param user User substrate address.
- /// @dev EVM selector for this function is: 0xd06ad267,
- /// or in textual repr: addToCollectionAllowListSubstrate(uint256)
- function addToCollectionAllowListSubstrate(uint256 user) external;
-
/// Remove the user from the allowed list.
///
/// @param user Address of a removed user.
@@ -188,13 +160,6 @@
/// or in textual repr: removeFromCollectionAllowList(address)
function removeFromCollectionAllowList(address user) external;
- /// Remove substrate user from allowed list.
- ///
- /// @param user User substrate address.
- /// @dev EVM selector for this function is: 0xa31913ed,
- /// or in textual repr: removeFromCollectionAllowListSubstrate(uint256)
- function removeFromCollectionAllowListSubstrate(uint256 user) external;
-
/// Switch permission for minting.
///
/// @param mode Enable if "true".
@@ -209,14 +174,6 @@
/// @dev EVM selector for this function is: 0x9811b0c7,
/// or in textual repr: isOwnerOrAdmin(address)
function isOwnerOrAdmin(address user) external view returns (bool);
-
- /// Check that substrate account is the owner or admin of the collection
- ///
- /// @param user account to verify
- /// @return "true" if account is the owner or admin
- /// @dev EVM selector for this function is: 0x68910e00,
- /// or in textual repr: isOwnerOrAdminSubstrate(uint256)
- function isOwnerOrAdminSubstrate(uint256 user) external view returns (bool);
/// Returns collection type
///
@@ -240,14 +197,6 @@
/// @dev EVM selector for this function is: 0x13af4035,
/// or in textual repr: setOwner(address)
function setOwner(address newOwner) external;
-
- /// Changes collection owner to another substrate account
- ///
- /// @dev Owner can be changed only by current owner
- /// @param newOwner new owner substrate account
- /// @dev EVM selector for this function is: 0xb212138f,
- /// or in textual repr: setOwnerSubstrate(uint256)
- function setOwnerSubstrate(uint256 newOwner) external;
}
/// @dev the ERC-165 identifier for this interface is 0x63034ac5
tests/src/eth/api/UniqueNFT.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueNFT.sol
+++ b/tests/src/eth/api/UniqueNFT.sol
@@ -62,7 +62,7 @@
}
/// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0x47dbc105
+/// @dev the ERC-165 identifier for this interface is 0x3e1e8083
interface Collection is Dummy, ERC165 {
/// Set collection property.
///
@@ -97,15 +97,6 @@
/// @dev EVM selector for this function is: 0x7623402e,
/// or in textual repr: setCollectionSponsor(address)
function setCollectionSponsor(address sponsor) external;
-
- /// Set the substrate sponsor of the collection.
- ///
- /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
- ///
- /// @param sponsor Substrate address of the sponsor from whose account funds will be debited for operations with the contract.
- /// @dev EVM selector for this function is: 0xc74d6751,
- /// or in textual repr: setCollectionSponsorSubstrate(uint256)
- function setCollectionSponsorSubstrate(uint256 sponsor) external;
/// Whether there is a pending sponsor.
/// @dev EVM selector for this function is: 0x058ac185,
@@ -161,18 +152,6 @@
/// or in textual repr: contractAddress()
function contractAddress() external view returns (address);
- /// Add collection admin by substrate address.
- /// @param newAdmin Substrate administrator address.
- /// @dev EVM selector for this function is: 0x5730062b,
- /// or in textual repr: addCollectionAdminSubstrate(uint256)
- function addCollectionAdminSubstrate(uint256 newAdmin) external;
-
- /// Remove collection admin by substrate address.
- /// @param admin Substrate administrator address.
- /// @dev EVM selector for this function is: 0x4048fcf9,
- /// or in textual repr: removeCollectionAdminSubstrate(uint256)
- function removeCollectionAdminSubstrate(uint256 admin) external;
-
/// Add collection admin.
/// @param newAdmin Address of the added administrator.
/// @dev EVM selector for this function is: 0x92e462c7,
@@ -223,13 +202,6 @@
/// or in textual repr: addToCollectionAllowList(address)
function addToCollectionAllowList(address user) external;
- /// Add substrate user to allowed list.
- ///
- /// @param user User substrate address.
- /// @dev EVM selector for this function is: 0xd06ad267,
- /// or in textual repr: addToCollectionAllowListSubstrate(uint256)
- function addToCollectionAllowListSubstrate(uint256 user) external;
-
/// Remove the user from the allowed list.
///
/// @param user Address of a removed user.
@@ -237,13 +209,6 @@
/// or in textual repr: removeFromCollectionAllowList(address)
function removeFromCollectionAllowList(address user) external;
- /// Remove substrate user from allowed list.
- ///
- /// @param user User substrate address.
- /// @dev EVM selector for this function is: 0xa31913ed,
- /// or in textual repr: removeFromCollectionAllowListSubstrate(uint256)
- function removeFromCollectionAllowListSubstrate(uint256 user) external;
-
/// Switch permission for minting.
///
/// @param mode Enable if "true".
@@ -258,14 +223,6 @@
/// @dev EVM selector for this function is: 0x9811b0c7,
/// or in textual repr: isOwnerOrAdmin(address)
function isOwnerOrAdmin(address user) external view returns (bool);
-
- /// Check that substrate account is the owner or admin of the collection
- ///
- /// @param user account to verify
- /// @return "true" if account is the owner or admin
- /// @dev EVM selector for this function is: 0x68910e00,
- /// or in textual repr: isOwnerOrAdminSubstrate(uint256)
- function isOwnerOrAdminSubstrate(uint256 user) external view returns (bool);
/// Returns collection type
///
@@ -289,14 +246,6 @@
/// @dev EVM selector for this function is: 0x13af4035,
/// or in textual repr: setOwner(address)
function setOwner(address newOwner) external;
-
- /// Changes collection owner to another substrate account
- ///
- /// @dev Owner can be changed only by current owner
- /// @param newOwner new owner substrate account
- /// @dev EVM selector for this function is: 0xb212138f,
- /// or in textual repr: setOwnerSubstrate(uint256)
- function setOwnerSubstrate(uint256 newOwner) external;
}
/// @dev anonymous struct
tests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueRefungible.sol
+++ b/tests/src/eth/api/UniqueRefungible.sol
@@ -62,7 +62,7 @@
}
/// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0x47dbc105
+/// @dev the ERC-165 identifier for this interface is 0x3e1e8083
interface Collection is Dummy, ERC165 {
/// Set collection property.
///
@@ -97,15 +97,6 @@
/// @dev EVM selector for this function is: 0x7623402e,
/// or in textual repr: setCollectionSponsor(address)
function setCollectionSponsor(address sponsor) external;
-
- /// Set the substrate sponsor of the collection.
- ///
- /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
- ///
- /// @param sponsor Substrate address of the sponsor from whose account funds will be debited for operations with the contract.
- /// @dev EVM selector for this function is: 0xc74d6751,
- /// or in textual repr: setCollectionSponsorSubstrate(uint256)
- function setCollectionSponsorSubstrate(uint256 sponsor) external;
/// Whether there is a pending sponsor.
/// @dev EVM selector for this function is: 0x058ac185,
@@ -161,18 +152,6 @@
/// or in textual repr: contractAddress()
function contractAddress() external view returns (address);
- /// Add collection admin by substrate address.
- /// @param newAdmin Substrate administrator address.
- /// @dev EVM selector for this function is: 0x5730062b,
- /// or in textual repr: addCollectionAdminSubstrate(uint256)
- function addCollectionAdminSubstrate(uint256 newAdmin) external;
-
- /// Remove collection admin by substrate address.
- /// @param admin Substrate administrator address.
- /// @dev EVM selector for this function is: 0x4048fcf9,
- /// or in textual repr: removeCollectionAdminSubstrate(uint256)
- function removeCollectionAdminSubstrate(uint256 admin) external;
-
/// Add collection admin.
/// @param newAdmin Address of the added administrator.
/// @dev EVM selector for this function is: 0x92e462c7,
@@ -223,13 +202,6 @@
/// or in textual repr: addToCollectionAllowList(address)
function addToCollectionAllowList(address user) external;
- /// Add substrate user to allowed list.
- ///
- /// @param user User substrate address.
- /// @dev EVM selector for this function is: 0xd06ad267,
- /// or in textual repr: addToCollectionAllowListSubstrate(uint256)
- function addToCollectionAllowListSubstrate(uint256 user) external;
-
/// Remove the user from the allowed list.
///
/// @param user Address of a removed user.
@@ -237,13 +209,6 @@
/// or in textual repr: removeFromCollectionAllowList(address)
function removeFromCollectionAllowList(address user) external;
- /// Remove substrate user from allowed list.
- ///
- /// @param user User substrate address.
- /// @dev EVM selector for this function is: 0xa31913ed,
- /// or in textual repr: removeFromCollectionAllowListSubstrate(uint256)
- function removeFromCollectionAllowListSubstrate(uint256 user) external;
-
/// Switch permission for minting.
///
/// @param mode Enable if "true".
@@ -258,14 +223,6 @@
/// @dev EVM selector for this function is: 0x9811b0c7,
/// or in textual repr: isOwnerOrAdmin(address)
function isOwnerOrAdmin(address user) external view returns (bool);
-
- /// Check that substrate account is the owner or admin of the collection
- ///
- /// @param user account to verify
- /// @return "true" if account is the owner or admin
- /// @dev EVM selector for this function is: 0x68910e00,
- /// or in textual repr: isOwnerOrAdminSubstrate(uint256)
- function isOwnerOrAdminSubstrate(uint256 user) external view returns (bool);
/// Returns collection type
///
@@ -289,14 +246,6 @@
/// @dev EVM selector for this function is: 0x13af4035,
/// or in textual repr: setOwner(address)
function setOwner(address newOwner) external;
-
- /// Changes collection owner to another substrate account
- ///
- /// @dev Owner can be changed only by current owner
- /// @param newOwner new owner substrate account
- /// @dev EVM selector for this function is: 0xb212138f,
- /// or in textual repr: setOwnerSubstrate(uint256)
- function setOwnerSubstrate(uint256 newOwner) external;
}
/// @dev anonymous struct
tests/src/eth/collectionSponsoring.test.tsdiffbeforeafterboth--- a/tests/src/eth/collectionSponsoring.test.ts
+++ b/tests/src/eth/collectionSponsoring.test.ts
@@ -40,25 +40,26 @@
]);
});
- itWeb3('Set substrate sponsor', async ({api, web3, privateKeyWrapper}) => {
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const collectionHelpers = evmCollectionHelpers(web3, owner);
- let result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send();
- const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
- const sponsor = privateKeyWrapper('//Alice');
- const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
+ // TODO: Temprorary off. Need refactor
+ // itWeb3('Set substrate sponsor', async ({api, web3, privateKeyWrapper}) => {
+ // const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ // const collectionHelpers = evmCollectionHelpers(web3, owner);
+ // let result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send();
+ // const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+ // const sponsor = privateKeyWrapper('//Alice');
+ // const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
- expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;
- result = await collectionEvm.methods.setCollectionSponsorSubstrate(sponsor.addressRaw).send({from: owner});
- expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true;
+ // expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;
+ // result = await collectionEvm.methods.setCollectionSponsorSubstrate(sponsor.addressRaw).send({from: owner});
+ // expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true;
- const confirmTx = await api.tx.unique.confirmSponsorship(collectionId);
- await submitTransactionAsync(sponsor, confirmTx);
- expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;
+ // const confirmTx = await api.tx.unique.confirmSponsorship(collectionId);
+ // await submitTransactionAsync(sponsor, confirmTx);
+ // expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;
- const sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner});
- expect(bigIntToSub(api, BigInt(sponsorTuple[1]))).to.be.eq(sponsor.address);
- });
+ // const sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner});
+ // expect(bigIntToSub(api, BigInt(sponsorTuple[1]))).to.be.eq(sponsor.address);
+ // });
itWeb3('Remove sponsor', async ({api, web3, privateKeyWrapper}) => {
const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
@@ -150,60 +151,61 @@
}
});
- itWeb3('Sponsoring collection from substrate address via access list', async ({api, web3, privateKeyWrapper}) => {
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const collectionHelpers = evmCollectionHelpers(web3, owner);
- const result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send();
- const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
- const sponsor = privateKeyWrapper('//Alice');
- const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
+ // TODO: Temprorary off. Need refactor
+ // itWeb3('Sponsoring collection from substrate address via access list', async ({api, web3, privateKeyWrapper}) => {
+ // const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ // const collectionHelpers = evmCollectionHelpers(web3, owner);
+ // const result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send();
+ // const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+ // const sponsor = privateKeyWrapper('//Alice');
+ // const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
- await collectionEvm.methods.setCollectionSponsorSubstrate(sponsor.addressRaw).send({from: owner});
+ // await collectionEvm.methods.setCollectionSponsorSubstrate(sponsor.addressRaw).send({from: owner});
- const confirmTx = await api.tx.unique.confirmSponsorship(collectionId);
- await submitTransactionAsync(sponsor, confirmTx);
+ // const confirmTx = await api.tx.unique.confirmSponsorship(collectionId);
+ // await submitTransactionAsync(sponsor, confirmTx);
- const user = createEthAccount(web3);
- const nextTokenId = await collectionEvm.methods.nextTokenId().call();
- expect(nextTokenId).to.be.equal('1');
+ // const user = createEthAccount(web3);
+ // const nextTokenId = await collectionEvm.methods.nextTokenId().call();
+ // expect(nextTokenId).to.be.equal('1');
- await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner});
- await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner});
- await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});
+ // await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner});
+ // await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner});
+ // await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});
- const ownerBalanceBefore = await ethBalanceViaSub(api, owner);
- const sponsorBalanceBefore = (await getBalance(api, [sponsor.address]))[0];
+ // const ownerBalanceBefore = await ethBalanceViaSub(api, owner);
+ // const sponsorBalanceBefore = (await getBalance(api, [sponsor.address]))[0];
- {
- const nextTokenId = await collectionEvm.methods.nextTokenId().call();
- expect(nextTokenId).to.be.equal('1');
- const result = await collectionEvm.methods.mintWithTokenURI(
- user,
- nextTokenId,
- 'Test URI',
- ).send({from: user});
- const events = normalizeEvents(result.events);
+ // {
+ // const nextTokenId = await collectionEvm.methods.nextTokenId().call();
+ // expect(nextTokenId).to.be.equal('1');
+ // const result = await collectionEvm.methods.mintWithTokenURI(
+ // user,
+ // nextTokenId,
+ // 'Test URI',
+ // ).send({from: user});
+ // const events = normalizeEvents(result.events);
- expect(events).to.be.deep.equal([
- {
- address: collectionIdAddress,
- event: 'Transfer',
- args: {
- from: '0x0000000000000000000000000000000000000000',
- to: user,
- tokenId: nextTokenId,
- },
- },
- ]);
+ // expect(events).to.be.deep.equal([
+ // {
+ // address: collectionIdAddress,
+ // event: 'Transfer',
+ // args: {
+ // from: '0x0000000000000000000000000000000000000000',
+ // to: user,
+ // tokenId: nextTokenId,
+ // },
+ // },
+ // ]);
- const ownerBalanceAfter = await ethBalanceViaSub(api, owner);
- const sponsorBalanceAfter = (await getBalance(api, [sponsor.address]))[0];
+ // const ownerBalanceAfter = await ethBalanceViaSub(api, owner);
+ // const sponsorBalanceAfter = (await getBalance(api, [sponsor.address]))[0];
- expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
- expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
- expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
- }
- });
+ // expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
+ // expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
+ // expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
+ // }
+ // });
itWeb3('Check that transaction via EVM spend money from sponsor address', async ({api, web3, privateKeyWrapper}) => {
const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
tests/src/eth/fungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/fungible.test.ts
+++ b/tests/src/eth/fungible.test.ts
@@ -14,204 +14,132 @@
// You should have received a copy of the GNU General Public License
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-import {approveExpectSuccess, createCollection, createCollectionExpectSuccess, createFungibleItemExpectSuccess, transferExpectSuccess, transferFromExpectSuccess, UNIQUE} from '../util/helpers';
-import {collectionIdToAddress, createEthAccount, createEthAccountWithBalance, evmCollection, GAS_ARGS, itWeb3, normalizeEvents, recordEthFee, recordEvents, subToEth, transferBalanceToEth} from './util/helpers';
-import fungibleAbi from './fungibleAbi.json';
-import {expect} from 'chai';
-import {submitTransactionAsync} from '../substrate/substrate-api';
+import {expect, itEth, usingEthPlaygrounds} from './util/playgrounds';
+import {IKeyringPair} from '@polkadot/types/types';
describe('Fungible: Information getting', () => {
- itWeb3('totalSupply', async ({api, web3, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- name: 'token name',
- mode: {type: 'Fungible', decimalPoints: 0},
+ let donor: IKeyringPair;
+ let alice: IKeyringPair;
+
+ before(async function() {
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ donor = privateKey('//Alice');
+ [alice] = await helper.arrange.createAccounts([20n], donor);
});
- const alice = privateKeyWrapper('//Alice');
+ });
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ itEth('totalSupply', async ({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+ const collection = await helper.ft.mintCollection(alice);
+ await collection.mint(alice, 200n);
- await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Substrate: alice.address});
-
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(fungibleAbi as any, address, {from: caller, ...GAS_ARGS});
+ const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'ft', caller);
const totalSupply = await contract.methods.totalSupply().call();
-
expect(totalSupply).to.equal('200');
});
- itWeb3('balanceOf', async ({api, web3, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- name: 'token name',
- mode: {type: 'Fungible', decimalPoints: 0},
- });
- const alice = privateKeyWrapper('//Alice');
+ itEth('balanceOf', async ({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+ const collection = await helper.ft.mintCollection(alice);
+ await collection.mint(alice, 200n, {Ethereum: caller});
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-
- await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Ethereum: caller});
-
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(fungibleAbi as any, address, {from: caller, ...GAS_ARGS});
+ const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'ft', caller);
const balance = await contract.methods.balanceOf(caller).call();
-
expect(balance).to.equal('200');
});
});
describe('Fungible: Plain calls', () => {
- itWeb3('Can perform mint()', async ({web3, api, privateKeyWrapper}) => {
- const alice = privateKeyWrapper('//Alice');
- const collection = await createCollection(api, alice, {
- name: 'token name',
- mode: {type: 'Fungible', decimalPoints: 0},
+ let donor: IKeyringPair;
+ let alice: IKeyringPair;
+
+ before(async function() {
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ donor = privateKey('//Alice');
+ [alice] = await helper.arrange.createAccounts([20n], donor);
});
+ });
- const receiver = createEthAccount(web3);
+ itEth('Can perform mint()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const receiver = helper.eth.createAccount();
+ const collection = await helper.ft.mintCollection(alice);
+ await collection.addAdmin(alice, {Ethereum: owner});
- const collectionIdAddress = collectionIdToAddress(collection.collectionId);
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const changeAdminTx = api.tx.unique.addCollectionAdmin(collection.collectionId, {Ethereum: owner});
- await submitTransactionAsync(alice, changeAdminTx);
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
- const collectionContract = evmCollection(web3, owner, collectionIdAddress, {type: 'Fungible', decimalPoints: 0});
- const result = await collectionContract.methods.mint(receiver, 100).send();
- const events = normalizeEvents(result.events);
+ const result = await contract.methods.mint(receiver, 100).send();
- expect(events).to.be.deep.equal([
- {
- address: collectionIdAddress,
- event: 'Transfer',
- args: {
- from: '0x0000000000000000000000000000000000000000',
- to: receiver,
- value: '100',
- },
- },
- ]);
+ const event = result.events.Transfer;
+ expect(event.address).to.equal(collectionAddress);
+ expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
+ expect(event.returnValues.to).to.equal(receiver);
+ expect(event.returnValues.value).to.equal('100');
});
-
- itWeb3('Can perform mintBulk()', async ({web3, api, privateKeyWrapper}) => {
- const alice = privateKeyWrapper('//Alice');
- const collection = await createCollection(api, alice, {
- name: 'token name',
- mode: {type: 'Fungible', decimalPoints: 0},
- });
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const receiver1 = createEthAccount(web3);
- const receiver2 = createEthAccount(web3);
- const receiver3 = createEthAccount(web3);
-
- const collectionIdAddress = collectionIdToAddress(collection.collectionId);
- const changeAdminTx = api.tx.unique.addCollectionAdmin(collection.collectionId, {Ethereum: owner});
- await submitTransactionAsync(alice, changeAdminTx);
+ itEth('Can perform mintBulk()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const bulkSize = 3;
+ const receivers = [...new Array(bulkSize)].map(() => helper.eth.createAccount());
+ const collection = await helper.ft.mintCollection(alice);
+ await collection.addAdmin(alice, {Ethereum: owner});
- const collectionContract = evmCollection(web3, owner, collectionIdAddress, {type: 'Fungible', decimalPoints: 0});
- const result = await collectionContract.methods.mintBulk([
- [receiver1, 10],
- [receiver2, 20],
- [receiver3, 30],
- ]).send();
- const events = normalizeEvents(result.events);
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
- expect(events).to.be.deep.contain({
- address:collectionIdAddress,
- event: 'Transfer',
- args: {
- from: '0x0000000000000000000000000000000000000000',
- to: receiver1,
- value: '10',
- },
- });
-
- expect(events).to.be.deep.contain({
- address:collectionIdAddress,
- event: 'Transfer',
- args: {
- from: '0x0000000000000000000000000000000000000000',
- to: receiver2,
- value: '20',
- },
- });
-
- expect(events).to.be.deep.contain({
- address:collectionIdAddress,
- event: 'Transfer',
- args: {
- from: '0x0000000000000000000000000000000000000000',
- to: receiver3,
- value: '30',
- },
- });
+ const result = await contract.methods.mintBulk(Array.from({length: bulkSize}, (_, i) => (
+ [receivers[i], (i + 1) * 10]
+ ))).send();
+ const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.value - b.returnValues.value);
+ for (let i = 0; i < bulkSize; i++) {
+ const event = events[i];
+ expect(event.address).to.equal(collectionAddress);
+ expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
+ expect(event.returnValues.to).to.equal(receivers[i]);
+ expect(event.returnValues.value).to.equal(String(10 * (i + 1)));
+ }
});
- itWeb3('Can perform burn()', async ({web3, api, privateKeyWrapper}) => {
- const alice = privateKeyWrapper('//Alice');
- const collection = await createCollection(api, alice, {
- name: 'token name',
- mode: {type: 'Fungible', decimalPoints: 0},
- });
-
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const changeAdminTx = api.tx.unique.addCollectionAdmin(collection.collectionId, {Ethereum: owner});
- await submitTransactionAsync(alice, changeAdminTx);
- const receiver = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ itEth('Can perform burn()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const receiver = await helper.eth.createAccountWithBalance(donor);
+ const collection = await helper.ft.mintCollection(alice);
+ await collection.addAdmin(alice, {Ethereum: owner});
- const collectionIdAddress = collectionIdToAddress(collection.collectionId);
- const collectionContract = evmCollection(web3, owner, collectionIdAddress, {type: 'Fungible', decimalPoints: 0});
- await collectionContract.methods.mint(receiver, 100).send();
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+ await contract.methods.mint(receiver, 100).send();
- const result = await collectionContract.methods.burnFrom(receiver, 49).send({from: receiver});
+ const result = await contract.methods.burnFrom(receiver, 49).send({from: receiver});
- const events = normalizeEvents(result.events);
-
- expect(events).to.be.deep.equal([
- {
- address: collectionIdAddress,
- event: 'Transfer',
- args: {
- from: receiver,
- to: '0x0000000000000000000000000000000000000000',
- value: '49',
- },
- },
- ]);
+ const event = result.events.Transfer;
+ expect(event.address).to.equal(collectionAddress);
+ expect(event.returnValues.from).to.equal(receiver);
+ expect(event.returnValues.to).to.equal('0x0000000000000000000000000000000000000000');
+ expect(event.returnValues.value).to.equal('49');
- const balance = await collectionContract.methods.balanceOf(receiver).call();
+ const balance = await contract.methods.balanceOf(receiver).call();
expect(balance).to.equal('51');
});
- itWeb3('Can perform approve()', async ({web3, api, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- name: 'token name',
- mode: {type: 'Fungible', decimalPoints: 0},
- });
- const alice = privateKeyWrapper('//Alice');
+ itEth('Can perform approve()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const spender = helper.eth.createAccount();
+ const collection = await helper.ft.mintCollection(alice);
+ await collection.mint(alice, 200n, {Ethereum: owner});
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-
- await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Ethereum: owner});
-
- const spender = createEthAccount(web3);
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(fungibleAbi as any, address, {from: owner, ...GAS_ARGS});
-
{
const result = await contract.methods.approve(spender, 100).send({from: owner});
- const events = normalizeEvents(result.events);
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Approval',
- args: {
- owner,
- spender,
- value: '100',
- },
- },
- ]);
+ const event = result.events.Approval;
+ expect(event.address).to.be.equal(collectionAddress);
+ expect(event.returnValues.owner).to.be.equal(owner);
+ expect(event.returnValues.spender).to.be.equal(spender);
+ expect(event.returnValues.value).to.be.equal('100');
}
{
@@ -220,51 +148,32 @@
}
});
- itWeb3('Can perform transferFrom()', async ({web3, api, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- name: 'token name',
- mode: {type: 'Fungible', decimalPoints: 0},
- });
- const alice = privateKeyWrapper('//Alice');
-
- const owner = createEthAccount(web3);
- await transferBalanceToEth(api, alice, owner);
-
- await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Ethereum: owner});
-
- const spender = createEthAccount(web3);
- await transferBalanceToEth(api, alice, spender);
-
- const receiver = createEthAccount(web3);
+ itEth('Can perform transferFrom()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const spender = await helper.eth.createAccountWithBalance(donor);
+ const receiver = helper.eth.createAccount();
+ const collection = await helper.ft.mintCollection(alice);
+ await collection.mint(alice, 200n, {Ethereum: owner});
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(fungibleAbi as any, address, {from: owner, ...GAS_ARGS});
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
await contract.methods.approve(spender, 100).send();
{
const result = await contract.methods.transferFrom(owner, receiver, 49).send({from: spender});
- const events = normalizeEvents(result.events);
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Transfer',
- args: {
- from: owner,
- to: receiver,
- value: '49',
- },
- },
- {
- address,
- event: 'Approval',
- args: {
- owner,
- spender,
- value: '51',
- },
- },
- ]);
+
+ let event = result.events.Transfer;
+ expect(event.address).to.be.equal(collectionAddress);
+ expect(event.returnValues.from).to.be.equal(owner);
+ expect(event.returnValues.to).to.be.equal(receiver);
+ expect(event.returnValues.value).to.be.equal('49');
+
+ event = result.events.Approval;
+ expect(event.address).to.be.equal(collectionAddress);
+ expect(event.returnValues.owner).to.be.equal(owner);
+ expect(event.returnValues.spender).to.be.equal(spender);
+ expect(event.returnValues.value).to.be.equal('51');
}
{
@@ -278,38 +187,23 @@
}
});
- itWeb3('Can perform transfer()', async ({web3, api, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- name: 'token name',
- mode: {type: 'Fungible', decimalPoints: 0},
- });
- const alice = privateKeyWrapper('//Alice');
-
- const owner = createEthAccount(web3);
- await transferBalanceToEth(api, alice, owner);
-
- await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Ethereum: owner});
-
- const receiver = createEthAccount(web3);
- await transferBalanceToEth(api, alice, receiver);
+ itEth('Can perform transfer()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const receiver = await helper.eth.createAccountWithBalance(donor);
+ const collection = await helper.ft.mintCollection(alice);
+ await collection.mint(alice, 200n, {Ethereum: owner});
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(fungibleAbi as any, address, {from: owner, ...GAS_ARGS});
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
{
const result = await contract.methods.transfer(receiver, 50).send({from: owner});
- const events = normalizeEvents(result.events);
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Transfer',
- args: {
- from: owner,
- to: receiver,
- value: '50',
- },
- },
- ]);
+
+ const event = result.events.Transfer;
+ expect(event.address).to.be.equal(collectionAddress);
+ expect(event.returnValues.from).to.be.equal(owner);
+ expect(event.returnValues.to).to.be.equal(receiver);
+ expect(event.returnValues.value).to.be.equal('50');
}
{
@@ -325,162 +219,141 @@
});
describe('Fungible: Fees', () => {
- itWeb3('approve() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- mode: {type: 'Fungible', decimalPoints: 0},
+ let donor: IKeyringPair;
+ let alice: IKeyringPair;
+
+ before(async function() {
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ donor = privateKey('//Alice');
+ [alice] = await helper.arrange.createAccounts([20n], donor);
});
- const alice = privateKeyWrapper('//Alice');
+ });
+
+ itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const spender = helper.eth.createAccount();
+ const collection = await helper.ft.mintCollection(alice);
+ await collection.mint(alice, 200n, {Ethereum: owner});
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const spender = createEthAccount(web3);
-
- await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Ethereum: owner});
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(fungibleAbi as any, address, {from: owner, ...GAS_ARGS});
-
- const cost = await recordEthFee(api, owner, () => contract.methods.approve(spender, 100).send({from: owner}));
- expect(cost < BigInt(0.2 * Number(UNIQUE)));
+ const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, 100).send({from: owner}));
+ expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
});
- itWeb3('transferFrom() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- mode: {type: 'Fungible', decimalPoints: 0},
- });
- const alice = privateKeyWrapper('//Alice');
-
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const spender = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-
- await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Ethereum: owner});
+ itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const spender = await helper.eth.createAccountWithBalance(donor);
+ const collection = await helper.ft.mintCollection(alice);
+ await collection.mint(alice, 200n, {Ethereum: owner});
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(fungibleAbi as any, address, {from: owner, ...GAS_ARGS});
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
await contract.methods.approve(spender, 100).send({from: owner});
- const cost = await recordEthFee(api, spender, () => contract.methods.transferFrom(owner, spender, 100).send({from: spender}));
- expect(cost < BigInt(0.2 * Number(UNIQUE)));
+ const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, 100).send({from: spender}));
+ expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
});
-
- itWeb3('transfer() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- mode: {type: 'Fungible', decimalPoints: 0},
- });
- const alice = privateKeyWrapper('//Alice');
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const receiver = createEthAccount(web3);
-
- await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Ethereum: owner});
+ itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const receiver = helper.eth.createAccount();
+ const collection = await helper.ft.mintCollection(alice);
+ await collection.mint(alice, 200n, {Ethereum: owner});
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(fungibleAbi as any, address, {from: owner, ...GAS_ARGS});
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
- const cost = await recordEthFee(api, owner, () => contract.methods.transfer(receiver, 100).send({from: owner}));
- expect(cost < BigInt(0.2 * Number(UNIQUE)));
+ const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, 100).send({from: owner}));
+ expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
});
});
describe('Fungible: Substrate calls', () => {
- itWeb3('Events emitted for approve()', async ({web3, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- mode: {type: 'Fungible', decimalPoints: 0},
+ let donor: IKeyringPair;
+ let alice: IKeyringPair;
+
+ before(async function() {
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ donor = privateKey('//Alice');
+ [alice] = await helper.arrange.createAccounts([20n], donor);
});
- const alice = privateKeyWrapper('//Alice');
+ });
- const receiver = createEthAccount(web3);
+ itEth('Events emitted for approve()', async ({helper}) => {
+ const receiver = helper.eth.createAccount();
+ const collection = await helper.ft.mintCollection(alice);
+ await collection.mint(alice, 200n);
- await createFungibleItemExpectSuccess(alice, collection, {Value: 200n});
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'ft');
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(fungibleAbi as any, address);
-
- const events = await recordEvents(contract, async () => {
- await approveExpectSuccess(collection, 0, alice, {Ethereum: receiver}, 100);
+ const events: any = [];
+ contract.events.allEvents((_: any, event: any) => {
+ events.push(event);
});
+ await collection.approveTokens(alice, {Ethereum: receiver}, 100n);
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Approval',
- args: {
- owner: subToEth(alice.address),
- spender: receiver,
- value: '100',
- },
- },
- ]);
+ const event = events[0];
+ expect(event.event).to.be.equal('Approval');
+ expect(event.address).to.be.equal(collectionAddress);
+ expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));
+ expect(event.returnValues.spender).to.be.equal(receiver);
+ expect(event.returnValues.value).to.be.equal('100');
});
- itWeb3('Events emitted for transferFrom()', async ({web3, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- mode: {type: 'Fungible', decimalPoints: 0},
- });
- const alice = privateKeyWrapper('//Alice');
- const bob = privateKeyWrapper('//Bob');
+ itEth('Events emitted for transferFrom()', async ({helper}) => {
+ const [bob] = await helper.arrange.createAccounts([10n], donor);
+ const receiver = helper.eth.createAccount();
+ const collection = await helper.ft.mintCollection(alice);
+ await collection.mint(alice, 200n);
+ await collection.approveTokens(alice, {Substrate: bob.address}, 100n);
- const receiver = createEthAccount(web3);
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'ft');
- await createFungibleItemExpectSuccess(alice, collection, {Value: 200n});
- await approveExpectSuccess(collection, 0, alice, bob.address, 100);
+ const events: any = [];
+ contract.events.allEvents((_: any, event: any) => {
+ events.push(event);
+ });
+ await collection.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver}, 51n);
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(fungibleAbi as any, address);
+ let event = events[0];
+ expect(event.event).to.be.equal('Transfer');
+ expect(event.address).to.be.equal(collectionAddress);
+ expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
+ expect(event.returnValues.to).to.be.equal(receiver);
+ expect(event.returnValues.value).to.be.equal('51');
- const events = await recordEvents(contract, async () => {
- await transferFromExpectSuccess(collection, 0, bob, alice, {Ethereum: receiver}, 51, 'Fungible');
- });
-
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Transfer',
- args: {
- from: subToEth(alice.address),
- to: receiver,
- value: '51',
- },
- },
- {
- address,
- event: 'Approval',
- args: {
- owner: subToEth(alice.address),
- spender: subToEth(bob.address),
- value: '49',
- },
- },
- ]);
+ event = events[1];
+ expect(event.event).to.be.equal('Approval');
+ expect(event.address).to.be.equal(collectionAddress);
+ expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));
+ expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(bob.address));
+ expect(event.returnValues.value).to.be.equal('49');
});
- itWeb3('Events emitted for transfer()', async ({web3, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- mode: {type: 'Fungible', decimalPoints: 0},
- });
- const alice = privateKeyWrapper('//Alice');
+ itEth('Events emitted for transfer()', async ({helper}) => {
+ const receiver = helper.eth.createAccount();
+ const collection = await helper.ft.mintCollection(alice);
+ await collection.mint(alice, 200n);
- const receiver = createEthAccount(web3);
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'ft');
- await createFungibleItemExpectSuccess(alice, collection, {Value: 200n});
-
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(fungibleAbi as any, address);
-
- const events = await recordEvents(contract, async () => {
- await transferExpectSuccess(collection, 0, alice, {Ethereum:receiver}, 51, 'Fungible');
+ const events: any = [];
+ contract.events.allEvents((_: any, event: any) => {
+ events.push(event);
});
+ await collection.transfer(alice, {Ethereum:receiver}, 51n);
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Transfer',
- args: {
- from: subToEth(alice.address),
- to: receiver,
- value: '51',
- },
- },
- ]);
+ const event = events[0];
+ expect(event.event).to.be.equal('Transfer');
+ expect(event.address).to.be.equal(collectionAddress);
+ expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
+ expect(event.returnValues.to).to.be.equal(receiver);
+ expect(event.returnValues.value).to.be.equal('51');
});
});
tests/src/eth/fungibleAbi.jsondiffbeforeafterboth--- a/tests/src/eth/fungibleAbi.json
+++ b/tests/src/eth/fungibleAbi.json
@@ -60,27 +60,9 @@
},
{
"inputs": [
- { "internalType": "uint256", "name": "newAdmin", "type": "uint256" }
- ],
- "name": "addCollectionAdminSubstrate",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
{ "internalType": "address", "name": "user", "type": "address" }
],
"name": "addToCollectionAllowList",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "user", "type": "uint256" }
- ],
- "name": "addToCollectionAllowListSubstrate",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
@@ -214,15 +196,6 @@
{ "internalType": "address", "name": "user", "type": "address" }
],
"name": "isOwnerOrAdmin",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "user", "type": "uint256" }
- ],
- "name": "isOwnerOrAdminSubstrate",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "view",
"type": "function"
@@ -271,15 +244,6 @@
"type": "function"
},
{
- "inputs": [
- { "internalType": "uint256", "name": "admin", "type": "uint256" }
- ],
- "name": "removeCollectionAdminSubstrate",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
"inputs": [],
"name": "removeCollectionSponsor",
"outputs": [],
@@ -291,15 +255,6 @@
{ "internalType": "address", "name": "user", "type": "address" }
],
"name": "removeFromCollectionAllowList",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "user", "type": "uint256" }
- ],
- "name": "removeFromCollectionAllowListSubstrate",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
@@ -380,27 +335,9 @@
},
{
"inputs": [
- { "internalType": "uint256", "name": "sponsor", "type": "uint256" }
- ],
- "name": "setCollectionSponsorSubstrate",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
{ "internalType": "address", "name": "newOwner", "type": "address" }
],
"name": "setOwner",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "newOwner", "type": "uint256" }
- ],
- "name": "setOwnerSubstrate",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
tests/src/eth/nonFungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/nonFungible.test.ts
+++ b/tests/src/eth/nonFungible.test.ts
@@ -14,72 +14,78 @@
// You should have received a copy of the GNU General Public License
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-import {approveExpectSuccess, burnItemExpectSuccess, createCollectionExpectSuccess, createItemExpectSuccess, transferExpectSuccess, transferFromExpectSuccess, UNIQUE} from '../util/helpers';
-import {collectionIdToAddress, createEthAccount, createEthAccountWithBalance, evmCollection, evmCollectionHelpers, GAS_ARGS, getCollectionAddressFromResult, itWeb3, normalizeEvents, recordEthFee, recordEvents, subToEth, transferBalanceToEth} from './util/helpers';
-import nonFungibleAbi from './nonFungibleAbi.json';
-import {expect} from 'chai';
-import {submitTransactionAsync} from '../substrate/substrate-api';
+import {itEth, usingEthPlaygrounds, expect, EthUniqueHelper} from './util/playgrounds';
+import {IKeyringPair} from '@polkadot/types/types';
+import {Contract} from 'web3-eth-contract';
describe('NFT: Information getting', () => {
- itWeb3('totalSupply', async ({api, web3, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- mode: {type: 'NFT'},
+ let donor: IKeyringPair;
+ let alice: IKeyringPair;
+
+ before(async function() {
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ donor = privateKey('//Alice');
+ [alice] = await helper.arrange.createAccounts([10n], donor);
});
- const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ });
+
+ itEth('totalSupply', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {});
+ await collection.mintToken(alice);
- await createItemExpectSuccess(alice, collection, 'NFT', {Substrate: alice.address});
+ const caller = await helper.eth.createAccountWithBalance(donor);
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS});
+ const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);
const totalSupply = await contract.methods.totalSupply().call();
expect(totalSupply).to.equal('1');
});
- itWeb3('balanceOf', async ({api, web3, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- mode: {type: 'NFT'},
- });
- const alice = privateKeyWrapper('//Alice');
+ itEth('balanceOf', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {});
+ const caller = await helper.eth.createAccountWithBalance(donor);
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum:caller});
- await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: caller});
- await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: caller});
+ await collection.mintToken(alice, {Ethereum: caller});
+ await collection.mintToken(alice, {Ethereum: caller});
+ await collection.mintToken(alice, {Ethereum: caller});
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS});
+ const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);
const balance = await contract.methods.balanceOf(caller).call();
expect(balance).to.equal('3');
});
- itWeb3('ownerOf', async ({api, web3, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- mode: {type: 'NFT'},
- });
- const alice = privateKeyWrapper('//Alice');
+ itEth('ownerOf', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {});
+ const caller = await helper.eth.createAccountWithBalance(donor);
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: caller});
+ const token = await collection.mintToken(alice, {Ethereum: caller});
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS});
- const owner = await contract.methods.ownerOf(tokenId).call();
+ const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);
+
+ const owner = await contract.methods.ownerOf(token.tokenId).call();
expect(owner).to.equal(caller);
});
});
describe('Check ERC721 token URI for NFT', () => {
- itWeb3('Empty tokenURI', async ({web3, api, privateKeyWrapper}) => {
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const helper = evmCollectionHelpers(web3, owner);
- let result = await helper.methods.createERC721MetadataCompatibleCollection('Mint collection', '1', '1', '').send();
- const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
- const receiver = createEthAccount(web3);
- const contract = evmCollection(web3, owner, collectionIdAddress);
+ let donor: IKeyringPair;
+
+ before(async function() {
+ await usingEthPlaygrounds(async (_helper, privateKey) => {
+ donor = privateKey('//Alice');
+ });
+ });
+
+ async function setup(helper: EthUniqueHelper, tokenPrefix: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const receiver = helper.eth.createAccount();
+
+ const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
+ let result = await collectionHelper.methods.createERC721MetadataCompatibleCollection('Mint collection', 'a', 'b', tokenPrefix).send();
+ const collectionAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
const nextTokenId = await contract.methods.nextTokenId().call();
expect(nextTokenId).to.be.equal('1');
@@ -88,162 +94,73 @@
nextTokenId,
).send();
- const events = normalizeEvents(result.events);
- const address = collectionIdToAddress(collectionId);
+ if (propertyKey && propertyValue) {
+ // Set URL or suffix
+ await contract.methods.setProperty(nextTokenId, propertyKey, Buffer.from(propertyValue)).send();
+ }
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Transfer',
- args: {
- from: '0x0000000000000000000000000000000000000000',
- to: receiver,
- tokenId: nextTokenId,
- },
- },
- ]);
+ const event = result.events.Transfer;
+ expect(event.address).to.be.equal(collectionAddress);
+ expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
+ expect(event.returnValues.to).to.be.equal(receiver);
+ expect(event.returnValues.tokenId).to.be.equal(nextTokenId);
+ return {contract, nextTokenId};
+ }
+
+ itEth('Empty tokenURI', async ({helper}) => {
+ const {contract, nextTokenId} = await setup(helper, '');
expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('');
});
- itWeb3('TokenURI from url', async ({web3, api, privateKeyWrapper}) => {
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const helper = evmCollectionHelpers(web3, owner);
- let result = await helper.methods.createERC721MetadataCompatibleCollection('Mint collection', '1', '1', 'BaseURI_').send();
- const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
- const receiver = createEthAccount(web3);
- const contract = evmCollection(web3, owner, collectionIdAddress);
-
- const nextTokenId = await contract.methods.nextTokenId().call();
- expect(nextTokenId).to.be.equal('1');
- result = await contract.methods.mint(
- receiver,
- nextTokenId,
- ).send();
-
- // Set URL
- await contract.methods.setProperty(nextTokenId, 'url', Buffer.from('Token URI')).send();
-
- const events = normalizeEvents(result.events);
- const address = collectionIdToAddress(collectionId);
-
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Transfer',
- args: {
- from: '0x0000000000000000000000000000000000000000',
- to: receiver,
- tokenId: nextTokenId,
- },
- },
- ]);
-
+ itEth('TokenURI from url', async ({helper}) => {
+ const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'url', 'Token URI');
expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI');
});
- itWeb3('TokenURI from baseURI + tokenId', async ({web3, api, privateKeyWrapper}) => {
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const helper = evmCollectionHelpers(web3, owner);
- let result = await helper.methods.createERC721MetadataCompatibleCollection('Mint collection', '1', '1', 'BaseURI_').send();
- const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
- const receiver = createEthAccount(web3);
- const contract = evmCollection(web3, owner, collectionIdAddress);
-
- const nextTokenId = await contract.methods.nextTokenId().call();
- expect(nextTokenId).to.be.equal('1');
- result = await contract.methods.mint(
- receiver,
- nextTokenId,
- ).send();
-
- const events = normalizeEvents(result.events);
- const address = collectionIdToAddress(collectionId);
-
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Transfer',
- args: {
- from: '0x0000000000000000000000000000000000000000',
- to: receiver,
- tokenId: nextTokenId,
- },
- },
- ]);
-
+ itEth('TokenURI from baseURI + tokenId', async ({helper}) => {
+ const {contract, nextTokenId} = await setup(helper, 'BaseURI_');
expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + nextTokenId);
});
- itWeb3('TokenURI from baseURI + suffix', async ({web3, api, privateKeyWrapper}) => {
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const helper = evmCollectionHelpers(web3, owner);
- let result = await helper.methods.createERC721MetadataCompatibleCollection('Mint collection', '1', '1', 'BaseURI_').send();
- const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
- const receiver = createEthAccount(web3);
- const contract = evmCollection(web3, owner, collectionIdAddress);
-
- const nextTokenId = await contract.methods.nextTokenId().call();
- expect(nextTokenId).to.be.equal('1');
- result = await contract.methods.mint(
- receiver,
- nextTokenId,
- ).send();
-
- // Set suffix
+ itEth('TokenURI from baseURI + suffix', async ({helper}) => {
const suffix = '/some/suffix';
- await contract.methods.setProperty(nextTokenId, 'suffix', Buffer.from(suffix)).send();
-
- const events = normalizeEvents(result.events);
- const address = collectionIdToAddress(collectionId);
-
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Transfer',
- args: {
- from: '0x0000000000000000000000000000000000000000',
- to: receiver,
- tokenId: nextTokenId,
- },
- },
- ]);
-
+ const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'suffix', suffix);
expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix);
});
});
describe('NFT: Plain calls', () => {
- itWeb3('Can perform mint()', async ({web3, api, privateKeyWrapper}) => {
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const helper = evmCollectionHelpers(web3, owner);
- let result = await helper.methods.createNonfungibleCollection('Mint collection', '6', '6').send();
- const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
- const receiver = createEthAccount(web3);
- const contract = evmCollection(web3, owner, collectionIdAddress);
+ let donor: IKeyringPair;
+ let alice: IKeyringPair;
+
+ before(async function() {
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ donor = privateKey('//Alice');
+ [alice] = await helper.arrange.createAccounts([10n], donor);
+ });
+ });
+
+ itEth('Can perform mint()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const receiver = helper.eth.createAccount();
+
+ const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'Minty', '6', '6');
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
const nextTokenId = await contract.methods.nextTokenId().call();
expect(nextTokenId).to.be.equal('1');
- result = await contract.methods.mintWithTokenURI(
+ const result = await contract.methods.mintWithTokenURI(
receiver,
nextTokenId,
'Test URI',
).send();
- const events = normalizeEvents(result.events);
- const address = collectionIdToAddress(collectionId);
-
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Transfer',
- args: {
- from: '0x0000000000000000000000000000000000000000',
- to: receiver,
- tokenId: nextTokenId,
- },
- },
- ]);
+ const event = result.events.Transfer;
+ expect(event.address).to.be.equal(collectionAddress);
+ expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
+ expect(event.returnValues.to).to.be.equal(receiver);
+ expect(event.returnValues.tokenId).to.be.equal(nextTokenId);
expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
@@ -254,7 +171,8 @@
});
//TODO: CORE-302 add eth methods
- itWeb3.skip('Can perform mintBulk()', async ({web3, api, privateKeyWrapper}) => {
+ /* todo:playgrounds skipped test!
+ itWeb3.skip('Can perform mintBulk()', async ({helper}) => {
const collection = await createCollectionExpectSuccess({
mode: {type: 'NFT'},
});
@@ -316,107 +234,70 @@
expect(await contract.methods.tokenURI(+nextTokenId + 2).call()).to.be.equal('Test URI 2');
}
});
-
- itWeb3('Can perform burn()', async ({web3, api, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- mode: {type: 'NFT'},
- });
- const alice = privateKeyWrapper('//Alice');
+ */
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ itEth('Can perform burn()', async ({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
- const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: owner});
+ const collection = await helper.nft.mintCollection(alice, {});
+ const {tokenId} = await collection.mintToken(alice, {Ethereum: caller});
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: owner, ...GAS_ARGS});
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', caller);
{
- const result = await contract.methods.burn(tokenId).send({from: owner});
- const events = normalizeEvents(result.events);
-
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Transfer',
- args: {
- from: owner,
- to: '0x0000000000000000000000000000000000000000',
- tokenId: tokenId.toString(),
- },
- },
- ]);
+ const result = await contract.methods.burn(tokenId).send({from: caller});
+
+ const event = result.events.Transfer;
+ expect(event.address).to.be.equal(collectionAddress);
+ expect(event.returnValues.from).to.be.equal(caller);
+ expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000');
+ expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);
}
});
- itWeb3('Can perform approve()', async ({web3, api, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- mode: {type: 'NFT'},
- });
- const alice = privateKeyWrapper('//Alice');
-
- const owner = createEthAccount(web3);
- await transferBalanceToEth(api, alice, owner);
-
- const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: owner});
+ itEth('Can perform approve()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const spender = helper.eth.createAccount();
- const spender = createEthAccount(web3);
+ const collection = await helper.nft.mintCollection(alice, {});
+ const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(nonFungibleAbi as any, address);
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
{
- const result = await contract.methods.approve(spender, tokenId).send({from: owner, ...GAS_ARGS});
- const events = normalizeEvents(result.events);
+ const result = await contract.methods.approve(spender, tokenId).send({from: owner});
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Approval',
- args: {
- owner,
- approved: spender,
- tokenId: tokenId.toString(),
- },
- },
- ]);
+ const event = result.events.Approval;
+ expect(event.address).to.be.equal(collectionAddress);
+ expect(event.returnValues.owner).to.be.equal(owner);
+ expect(event.returnValues.approved).to.be.equal(spender);
+ expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);
}
});
- itWeb3('Can perform transferFrom()', async ({web3, api, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- mode: {type: 'NFT'},
- });
- const alice = privateKeyWrapper('//Alice');
-
- const owner = createEthAccount(web3);
- await transferBalanceToEth(api, alice, owner);
+ itEth('Can perform transferFrom()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const spender = await helper.eth.createAccountWithBalance(donor);
+ const receiver = helper.eth.createAccount();
- const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: owner});
-
- const spender = createEthAccount(web3);
- await transferBalanceToEth(api, alice, spender);
-
- const receiver = createEthAccount(web3);
+ const collection = await helper.nft.mintCollection(alice, {});
+ const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: owner, ...GAS_ARGS});
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
await contract.methods.approve(spender, tokenId).send({from: owner});
{
const result = await contract.methods.transferFrom(owner, receiver, tokenId).send({from: spender});
- const events = normalizeEvents(result.events);
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Transfer',
- args: {
- from: owner,
- to: receiver,
- tokenId: tokenId.toString(),
- },
- },
- ]);
+
+ const event = result.events.Transfer;
+ expect(event.address).to.be.equal(collectionAddress);
+ expect(event.returnValues.from).to.be.equal(owner);
+ expect(event.returnValues.to).to.be.equal(receiver);
+ expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);
}
{
@@ -430,37 +311,24 @@
}
});
- itWeb3('Can perform transfer()', async ({web3, api, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- mode: {type: 'NFT'},
- });
- const alice = privateKeyWrapper('//Alice');
-
- const owner = createEthAccount(web3);
- await transferBalanceToEth(api, alice, owner);
-
- const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: owner});
+ itEth('Can perform transfer()', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {});
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const receiver = helper.eth.createAccount();
- const receiver = createEthAccount(web3);
- await transferBalanceToEth(api, alice, receiver);
+ const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: owner, ...GAS_ARGS});
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
{
const result = await contract.methods.transfer(receiver, tokenId).send({from: owner});
- const events = normalizeEvents(result.events);
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Transfer',
- args: {
- from: owner,
- to: receiver,
- tokenId: tokenId.toString(),
- },
- },
- ]);
+
+ const event = result.events.Transfer;
+ expect(event.address).to.be.equal(collectionAddress);
+ expect(event.returnValues.from).to.be.equal(owner);
+ expect(event.returnValues.to).to.be.equal(receiver);
+ expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);
}
{
@@ -476,237 +344,209 @@
});
describe('NFT: Fees', () => {
- itWeb3('approve() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- mode: {type: 'NFT'},
+ let donor: IKeyringPair;
+ let alice: IKeyringPair;
+
+ before(async function() {
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ donor = privateKey('//Alice');
+ [alice] = await helper.arrange.createAccounts([10n], donor);
});
- const alice = privateKeyWrapper('//Alice');
+ });
+
+ itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const spender = helper.eth.createAccount();
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const spender = createEthAccount(web3);
+ const collection = await helper.nft.mintCollection(alice, {});
+ const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});
- const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: owner});
+ const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: owner, ...GAS_ARGS});
-
- const cost = await recordEthFee(api, owner, () => contract.methods.approve(spender, tokenId).send({from: owner}));
- expect(cost < BigInt(0.2 * Number(UNIQUE)));
+ const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, tokenId).send({from: owner}));
+ expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
});
- itWeb3('transferFrom() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- mode: {type: 'NFT'},
- });
- const alice = privateKeyWrapper('//Alice');
-
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const spender = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const spender = await helper.eth.createAccountWithBalance(donor);
- const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: owner});
+ const collection = await helper.nft.mintCollection(alice, {});
+ const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: owner, ...GAS_ARGS});
+ const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);
await contract.methods.approve(spender, tokenId).send({from: owner});
- const cost = await recordEthFee(api, spender, () => contract.methods.transferFrom(owner, spender, tokenId).send({from: spender}));
- expect(cost < BigInt(0.2 * Number(UNIQUE)));
+ const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, tokenId).send({from: spender}));
+ expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
});
- itWeb3('transfer() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- mode: {type: 'NFT'},
- });
- const alice = privateKeyWrapper('//Alice');
+ itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const receiver = helper.eth.createAccount();
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const receiver = createEthAccount(web3);
+ const collection = await helper.nft.mintCollection(alice, {});
+ const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});
- const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: owner});
+ const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: owner, ...GAS_ARGS});
-
- const cost = await recordEthFee(api, owner, () => contract.methods.transfer(receiver, tokenId).send({from: owner}));
- expect(cost < BigInt(0.2 * Number(UNIQUE)));
+ const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, tokenId).send({from: owner}));
+ expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
});
});
describe('NFT: Substrate calls', () => {
- itWeb3('Events emitted for mint()', async ({web3, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- mode: {type: 'NFT'},
+ let donor: IKeyringPair;
+ let alice: IKeyringPair;
+
+ before(async function() {
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ donor = privateKey('//Alice');
+ [alice] = await helper.arrange.createAccounts([20n], donor);
});
- const alice = privateKeyWrapper('//Alice');
+ });
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(nonFungibleAbi as any, address);
+ itEth('Events emitted for mint()', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {});
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');
- let tokenId: number;
- const events = await recordEvents(contract, async () => {
- tokenId = await createItemExpectSuccess(alice, collection, 'NFT');
+ const events: any = [];
+ contract.events.allEvents((_: any, event: any) => {
+ events.push(event);
});
+ const {tokenId} = await collection.mintToken(alice);
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Transfer',
- args: {
- from: '0x0000000000000000000000000000000000000000',
- to: subToEth(alice.address),
- tokenId: tokenId!.toString(),
- },
- },
- ]);
+ const event = events[0];
+ expect(event.event).to.be.equal('Transfer');
+ expect(event.address).to.be.equal(collectionAddress);
+ expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
+ expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(alice.address));
+ expect(event.returnValues.tokenId).to.be.equal(tokenId.toString());
});
- itWeb3('Events emitted for burn()', async ({web3, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- mode: {type: 'NFT'},
+ itEth('Events emitted for burn()', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {});
+ const token = await collection.mintToken(alice);
+
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');
+
+ const events: any = [];
+ contract.events.allEvents((_: any, event: any) => {
+ events.push(event);
});
- const alice = privateKeyWrapper('//Alice');
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(nonFungibleAbi as any, address);
+ await token.burn(alice);
- const tokenId = await createItemExpectSuccess(alice, collection, 'NFT');
- const events = await recordEvents(contract, async () => {
- await burnItemExpectSuccess(alice, collection, tokenId);
- });
-
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Transfer',
- args: {
- from: subToEth(alice.address),
- to: '0x0000000000000000000000000000000000000000',
- tokenId: tokenId.toString(),
- },
- },
- ]);
+ const event = events[0];
+ expect(event.event).to.be.equal('Transfer');
+ expect(event.address).to.be.equal(collectionAddress);
+ expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
+ expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000');
+ expect(event.returnValues.tokenId).to.be.equal(token.tokenId.toString());
});
- itWeb3('Events emitted for approve()', async ({web3, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- mode: {type: 'NFT'},
- });
- const alice = privateKeyWrapper('//Alice');
+ itEth('Events emitted for approve()', async ({helper}) => {
+ const receiver = helper.eth.createAccount();
- const receiver = createEthAccount(web3);
-
- const tokenId = await createItemExpectSuccess(alice, collection, 'NFT');
-
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(nonFungibleAbi as any, address);
+ const collection = await helper.nft.mintCollection(alice, {});
+ const token = await collection.mintToken(alice);
- const events = await recordEvents(contract, async () => {
- await approveExpectSuccess(collection, tokenId, alice, {Ethereum: receiver}, 1);
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');
+
+ const events: any = [];
+ contract.events.allEvents((_: any, event: any) => {
+ events.push(event);
});
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Approval',
- args: {
- owner: subToEth(alice.address),
- approved: receiver,
- tokenId: tokenId.toString(),
- },
- },
- ]);
+ await token.approve(alice, {Ethereum: receiver});
+
+ const event = events[0];
+ expect(event.event).to.be.equal('Approval');
+ expect(event.address).to.be.equal(collectionAddress);
+ expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));
+ expect(event.returnValues.approved).to.be.equal(receiver);
+ expect(event.returnValues.tokenId).to.be.equal(token.tokenId.toString());
});
- itWeb3('Events emitted for transferFrom()', async ({web3, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- mode: {type: 'NFT'},
- });
- const alice = privateKeyWrapper('//Alice');
- const bob = privateKeyWrapper('//Bob');
+ itEth('Events emitted for transferFrom()', async ({helper}) => {
+ const [bob] = await helper.arrange.createAccounts([10n], donor);
+ const receiver = helper.eth.createAccount();
- const receiver = createEthAccount(web3);
-
- const tokenId = await createItemExpectSuccess(alice, collection, 'NFT');
- await approveExpectSuccess(collection, tokenId, alice, bob.address, 1);
-
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(nonFungibleAbi as any, address);
+ const collection = await helper.nft.mintCollection(alice, {});
+ const token = await collection.mintToken(alice);
+ await token.approve(alice, {Substrate: bob.address});
- const events = await recordEvents(contract, async () => {
- await transferFromExpectSuccess(collection, tokenId, bob, alice, {Ethereum: receiver}, 1, 'NFT');
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');
+
+ const events: any = [];
+ contract.events.allEvents((_: any, event: any) => {
+ events.push(event);
});
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Transfer',
- args: {
- from: subToEth(alice.address),
- to: receiver,
- tokenId: tokenId.toString(),
- },
- },
- ]);
+ await token.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver});
+
+ const event = events[0];
+ expect(event.address).to.be.equal(collectionAddress);
+ expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
+ expect(event.returnValues.to).to.be.equal(receiver);
+ expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`);
});
- itWeb3('Events emitted for transfer()', async ({web3, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- mode: {type: 'NFT'},
- });
- const alice = privateKeyWrapper('//Alice');
+ itEth('Events emitted for transfer()', async ({helper}) => {
+ const receiver = helper.eth.createAccount();
- const receiver = createEthAccount(web3);
+ const collection = await helper.nft.mintCollection(alice, {});
+ const token = await collection.mintToken(alice);
- const tokenId = await createItemExpectSuccess(alice, collection, 'NFT');
-
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(nonFungibleAbi as any, address);
-
- const events = await recordEvents(contract, async () => {
- await transferExpectSuccess(collection, tokenId, alice, {Ethereum: receiver}, 1, 'NFT');
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');
+
+ const events: any = [];
+ contract.events.allEvents((_: any, event: any) => {
+ events.push(event);
});
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Transfer',
- args: {
- from: subToEth(alice.address),
- to: receiver,
- tokenId: tokenId.toString(),
- },
- },
- ]);
+ await token.transfer(alice, {Ethereum: receiver});
+
+ const event = events[0];
+ expect(event.address).to.be.equal(collectionAddress);
+ expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
+ expect(event.returnValues.to).to.be.equal(receiver);
+ expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`);
});
});
describe('Common metadata', () => {
- itWeb3('Returns collection name', async ({api, web3, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- name: 'token name',
- mode: {type: 'NFT'},
+ let donor: IKeyringPair;
+ let alice: IKeyringPair;
+
+ before(async function() {
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ donor = privateKey('//Alice');
+ [alice] = await helper.arrange.createAccounts([20n], donor);
});
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ });
+
+ itEth('Returns collection name', async ({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+ const collection = await helper.nft.mintCollection(alice, {name: 'oh River', tokenPrefix: 'CHANGE'});
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS});
+ const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);
const name = await contract.methods.name().call();
-
- expect(name).to.equal('token name');
+ expect(name).to.equal('oh River');
});
- itWeb3('Returns symbol name', async ({api, web3, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- tokenPrefix: 'TOK',
- mode: {type: 'NFT'},
- });
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ itEth('Returns symbol name', async ({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+ const collection = await helper.nft.mintCollection(alice, {name: 'oh River', tokenPrefix: 'CHANGE'});
- const address = collectionIdToAddress(collection);
- const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS});
+ const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);
const symbol = await contract.methods.symbol().call();
-
- expect(symbol).to.equal('TOK');
+ expect(symbol).to.equal('CHANGE');
});
});
\ No newline at end of file
tests/src/eth/nonFungibleAbi.jsondiffbeforeafterboth--- a/tests/src/eth/nonFungibleAbi.json
+++ b/tests/src/eth/nonFungibleAbi.json
@@ -91,27 +91,9 @@
},
{
"inputs": [
- { "internalType": "uint256", "name": "newAdmin", "type": "uint256" }
- ],
- "name": "addCollectionAdminSubstrate",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
{ "internalType": "address", "name": "user", "type": "address" }
],
"name": "addToCollectionAllowList",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "user", "type": "uint256" }
- ],
- "name": "addToCollectionAllowListSubstrate",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
@@ -273,15 +255,6 @@
{ "internalType": "address", "name": "user", "type": "address" }
],
"name": "isOwnerOrAdmin",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "user", "type": "uint256" }
- ],
- "name": "isOwnerOrAdminSubstrate",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "view",
"type": "function"
@@ -385,15 +358,6 @@
"type": "function"
},
{
- "inputs": [
- { "internalType": "uint256", "name": "admin", "type": "uint256" }
- ],
- "name": "removeCollectionAdminSubstrate",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
"inputs": [],
"name": "removeCollectionSponsor",
"outputs": [],
@@ -405,15 +369,6 @@
{ "internalType": "address", "name": "user", "type": "address" }
],
"name": "removeFromCollectionAllowList",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "user", "type": "uint256" }
- ],
- "name": "removeFromCollectionAllowListSubstrate",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
@@ -527,27 +482,9 @@
},
{
"inputs": [
- { "internalType": "uint256", "name": "sponsor", "type": "uint256" }
- ],
- "name": "setCollectionSponsorSubstrate",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
{ "internalType": "address", "name": "newOwner", "type": "address" }
],
"name": "setOwner",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "newOwner", "type": "uint256" }
- ],
- "name": "setOwnerSubstrate",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
tests/src/eth/payable.test.tsdiffbeforeafterboth--- a/tests/src/eth/payable.test.ts
+++ b/tests/src/eth/payable.test.ts
@@ -50,7 +50,7 @@
await helper.eth.transferBalanceFromSubstrate(alice, helper.address.substrateToEth(alice.address), 5n);
- await helper.eth.callEVM(alice, contract.options.address, contract.methods.giveMoney().encodeABI(), weiCount);
+ await helper.eth.sendEVM(alice, contract.options.address, contract.methods.giveMoney().encodeABI(), weiCount);
expect(await contract.methods.getCollected().call()).to.be.equal(weiCount);
});
tests/src/eth/reFungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/reFungible.test.ts
+++ b/tests/src/eth/reFungible.test.ts
@@ -14,33 +14,35 @@
// You should have received a copy of the GNU General Public License
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-import {createCollectionExpectSuccess, UNIQUE, requirePallets, Pallets} from '../util/helpers';
-import {collectionIdToAddress, createEthAccount, createEthAccountWithBalance, evmCollection, evmCollectionHelpers, getCollectionAddressFromResult, itWeb3, normalizeEvents, recordEthFee, recordEvents, tokenIdToAddress, uniqueRefungibleToken} from './util/helpers';
-import {expect} from 'chai';
+import {Pallets, requirePalletsOrSkip} from '../util/playgrounds';
+import {expect, itEth, usingEthPlaygrounds} from './util/playgrounds';
+import {IKeyringPair} from '@polkadot/types/types';
describe('Refungible: Information getting', () => {
+ let donor: IKeyringPair;
+
before(async function() {
- await requirePallets(this, [Pallets.ReFungible]);
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+
+ donor = privateKey('//Alice');
+ });
});
- itWeb3('totalSupply', async ({api, web3, privateKeyWrapper}) => {
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const helper = evmCollectionHelpers(web3, caller);
- const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
- const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
- const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
+ itEth('totalSupply', async ({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+ const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'TotalSupply', '6', '6');
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
const nextTokenId = await contract.methods.nextTokenId().call();
await contract.methods.mint(caller, nextTokenId).send();
const totalSupply = await contract.methods.totalSupply().call();
expect(totalSupply).to.equal('1');
});
- itWeb3('balanceOf', async ({api, web3, privateKeyWrapper}) => {
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const helper = evmCollectionHelpers(web3, caller);
- const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
- const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
- const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
+ itEth('balanceOf', async ({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+ const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'BalanceOf', '6', '6');
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
{
const nextTokenId = await contract.methods.nextTokenId().call();
@@ -56,38 +58,30 @@
}
const balance = await contract.methods.balanceOf(caller).call();
-
expect(balance).to.equal('3');
});
- itWeb3('ownerOf', async ({api, web3, privateKeyWrapper}) => {
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const helper = evmCollectionHelpers(web3, caller);
- const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
- const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
- const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
+ itEth('ownerOf', async ({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+ const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'OwnerOf', '6', '6');
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
const tokenId = await contract.methods.nextTokenId().call();
await contract.methods.mint(caller, tokenId).send();
const owner = await contract.methods.ownerOf(tokenId).call();
-
expect(owner).to.equal(caller);
});
- itWeb3('ownerOf after burn', async ({api, web3, privateKeyWrapper}) => {
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const receiver = createEthAccount(web3);
- const helper = evmCollectionHelpers(web3, caller);
- const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
- const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
- const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
+ itEth('ownerOf after burn', async ({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+ const receiver = helper.eth.createAccount();
+ const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'OwnerOf-AfterBurn', '6', '6');
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
const tokenId = await contract.methods.nextTokenId().call();
await contract.methods.mint(caller, tokenId).send();
-
- const tokenAddress = tokenIdToAddress(collectionId, tokenId);
- const tokenContract = uniqueRefungibleToken(web3, tokenAddress, caller);
+ const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);
await tokenContract.methods.repartition(2).send();
await tokenContract.methods.transfer(receiver, 1).send();
@@ -95,80 +89,67 @@
await tokenContract.methods.burnFrom(caller, 1).send();
const owner = await contract.methods.ownerOf(tokenId).call();
-
expect(owner).to.equal(receiver);
});
- itWeb3('ownerOf for partial ownership', async ({api, web3, privateKeyWrapper}) => {
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const receiver = createEthAccount(web3);
- const helper = evmCollectionHelpers(web3, caller);
- const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
- const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
- const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
+ itEth('ownerOf for partial ownership', async ({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+ const receiver = helper.eth.createAccount();
+ const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Partial-OwnerOf', '6', '6');
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
const tokenId = await contract.methods.nextTokenId().call();
await contract.methods.mint(caller, tokenId).send();
-
- const tokenAddress = tokenIdToAddress(collectionId, tokenId);
- const tokenContract = uniqueRefungibleToken(web3, tokenAddress, caller);
+ const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);
await tokenContract.methods.repartition(2).send();
await tokenContract.methods.transfer(receiver, 1).send();
const owner = await contract.methods.ownerOf(tokenId).call();
-
expect(owner).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');
});
});
describe('Refungible: Plain calls', () => {
+ let donor: IKeyringPair;
+
before(async function() {
- await requirePallets(this, [Pallets.ReFungible]);
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+
+ donor = privateKey('//Alice');
+ });
});
- itWeb3('Can perform mint()', async ({web3, api, privateKeyWrapper}) => {
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const helper = evmCollectionHelpers(web3, owner);
- let result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
- const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
- const receiver = createEthAccount(web3);
- const contract = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'});
+ itEth('Can perform mint()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const receiver = helper.eth.createAccount();
+ const {collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Minty', '6', '6');
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+
const nextTokenId = await contract.methods.nextTokenId().call();
-
expect(nextTokenId).to.be.equal('1');
- result = await contract.methods.mintWithTokenURI(
+ const result = await contract.methods.mintWithTokenURI(
receiver,
nextTokenId,
'Test URI',
).send();
- const events = normalizeEvents(result.events);
-
- expect(events).to.include.deep.members([
- {
- address: collectionIdAddress,
- event: 'Transfer',
- args: {
- from: '0x0000000000000000000000000000000000000000',
- to: receiver,
- tokenId: nextTokenId,
- },
- },
- ]);
+ const event = result.events.Transfer;
+ expect(event.address).to.equal(collectionAddress);
+ expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
+ expect(event.returnValues.to).to.equal(receiver);
+ expect(event.returnValues.tokenId).to.equal(nextTokenId);
expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
});
- itWeb3('Can perform mintBulk()', async ({web3, api, privateKeyWrapper}) => {
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const helper = evmCollectionHelpers(web3, caller);
- const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
- const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
- const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
+ itEth('Can perform mintBulk()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const receiver = helper.eth.createAccount();
+ const {collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'MintBulky', '6', '6');
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
- const receiver = createEthAccount(web3);
-
{
const nextTokenId = await contract.methods.nextTokenId().call();
expect(nextTokenId).to.be.equal('1');
@@ -180,37 +161,15 @@
[+nextTokenId + 2, 'Test URI 2'],
],
).send();
- const events = normalizeEvents(result.events);
- expect(events).to.include.deep.members([
- {
- address: collectionIdAddress,
- event: 'Transfer',
- args: {
- from: '0x0000000000000000000000000000000000000000',
- to: receiver,
- tokenId: nextTokenId,
- },
- },
- {
- address: collectionIdAddress,
- event: 'Transfer',
- args: {
- from: '0x0000000000000000000000000000000000000000',
- to: receiver,
- tokenId: String(+nextTokenId + 1),
- },
- },
- {
- address: collectionIdAddress,
- event: 'Transfer',
- args: {
- from: '0x0000000000000000000000000000000000000000',
- to: receiver,
- tokenId: String(+nextTokenId + 2),
- },
- },
- ]);
+ const events = result.events.Transfer;
+ for (let i = 0; i < 2; i++) {
+ const event = events[i];
+ expect(event.address).to.equal(collectionAddress);
+ expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
+ expect(event.returnValues.to).to.equal(receiver);
+ expect(event.returnValues.tokenId).to.equal(String(+nextTokenId + i));
+ }
expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI 0');
expect(await contract.methods.tokenURI(+nextTokenId + 1).call()).to.be.equal('Test URI 1');
@@ -218,76 +177,54 @@
}
});
- itWeb3('Can perform burn()', async ({web3, api, privateKeyWrapper}) => {
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const helper = evmCollectionHelpers(web3, caller);
- const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
- const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
- const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
+ itEth('Can perform burn()', async ({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+ const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Burny', '6', '6');
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
const tokenId = await contract.methods.nextTokenId().call();
await contract.methods.mint(caller, tokenId).send();
{
const result = await contract.methods.burn(tokenId).send();
- const events = normalizeEvents(result.events);
- expect(events).to.include.deep.members([
- {
- address: collectionIdAddress,
- event: 'Transfer',
- args: {
- from: caller,
- to: '0x0000000000000000000000000000000000000000',
- tokenId: tokenId.toString(),
- },
- },
- ]);
+ const event = result.events.Transfer;
+ expect(event.address).to.equal(collectionAddress);
+ expect(event.returnValues.from).to.equal(caller);
+ expect(event.returnValues.to).to.equal('0x0000000000000000000000000000000000000000');
+ expect(event.returnValues.tokenId).to.equal(tokenId.toString());
}
});
- itWeb3('Can perform transferFrom()', async ({web3, api, privateKeyWrapper}) => {
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const helper = evmCollectionHelpers(web3, caller);
- const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
- const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
- const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
-
- const receiver = createEthAccount(web3);
+ itEth('Can perform transferFrom()', async ({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+ const receiver = helper.eth.createAccount();
+ const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'TransferFromy', '6', '6');
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
const tokenId = await contract.methods.nextTokenId().call();
+ const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);
await contract.methods.mint(caller, tokenId).send();
- const address = tokenIdToAddress(collectionId, tokenId);
- const tokenContract = uniqueRefungibleToken(web3, address, caller);
+ const tokenContract = helper.ethNativeContract.rftToken(tokenAddress, caller);
await tokenContract.methods.repartition(15).send();
{
- const erc20Events = await recordEvents(tokenContract, async () => {
- const result = await contract.methods.transferFrom(caller, receiver, tokenId).send();
- const events = normalizeEvents(result.events);
- expect(events).to.include.deep.members([
- {
- address: collectionIdAddress,
- event: 'Transfer',
- args: {
- from: caller,
- to: receiver,
- tokenId: tokenId.toString(),
- },
- },
- ]);
+ const tokenEvents: any = [];
+ tokenContract.events.allEvents((_: any, event: any) => {
+ tokenEvents.push(event);
});
+ const result = await contract.methods.transferFrom(caller, receiver, tokenId).send();
+
+ let event = result.events.Transfer;
+ expect(event.address).to.equal(collectionAddress);
+ expect(event.returnValues.from).to.equal(caller);
+ expect(event.returnValues.to).to.equal(receiver);
+ expect(event.returnValues.tokenId).to.equal(tokenId.toString());
- expect(erc20Events).to.include.deep.members([
- {
- address,
- event: 'Transfer',
- args: {
- from: caller,
- to: receiver,
- value: '15',
- },
- },
- ]);
+ event = tokenEvents[0];
+ expect(event.address).to.equal(tokenAddress);
+ expect(event.returnValues.from).to.equal(caller);
+ expect(event.returnValues.to).to.equal(receiver);
+ expect(event.returnValues.value).to.equal('15');
}
{
@@ -301,32 +238,23 @@
}
});
- itWeb3('Can perform transfer()', async ({web3, api, privateKeyWrapper}) => {
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const helper = evmCollectionHelpers(web3, caller);
- const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
- const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
- const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
-
- const receiver = createEthAccount(web3);
+ itEth('Can perform transfer()', async ({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+ const receiver = helper.eth.createAccount();
+ const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Transferry', '6', '6');
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
const tokenId = await contract.methods.nextTokenId().call();
await contract.methods.mint(caller, tokenId).send();
{
const result = await contract.methods.transfer(receiver, tokenId).send();
- const events = normalizeEvents(result.events);
- expect(events).to.include.deep.members([
- {
- address: collectionIdAddress,
- event: 'Transfer',
- args: {
- from: caller,
- to: receiver,
- tokenId: tokenId.toString(),
- },
- },
- ]);
+
+ const event = result.events.Transfer;
+ expect(event.address).to.equal(collectionAddress);
+ expect(event.returnValues.from).to.equal(caller);
+ expect(event.returnValues.to).to.equal(receiver);
+ expect(event.returnValues.tokenId).to.equal(tokenId.toString());
}
{
@@ -340,141 +268,127 @@
}
});
- itWeb3('transfer event on transfer from partial ownership to full ownership', async ({api, web3, privateKeyWrapper}) => {
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const receiver = createEthAccount(web3);
- const helper = evmCollectionHelpers(web3, caller);
- const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
- const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
- const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
+ itEth('transfer event on transfer from partial ownership to full ownership', async ({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+ const receiver = helper.eth.createAccount();
+ const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Transferry-Partial-to-Full', '6', '6');
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
const tokenId = await contract.methods.nextTokenId().call();
await contract.methods.mint(caller, tokenId).send();
- const tokenAddress = tokenIdToAddress(collectionId, tokenId);
- const tokenContract = uniqueRefungibleToken(web3, tokenAddress, caller);
+ const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);
await tokenContract.methods.repartition(2).send();
await tokenContract.methods.transfer(receiver, 1).send();
- const events = await recordEvents(contract, async () =>
- await tokenContract.methods.transfer(receiver, 1).send());
- expect(events).to.deep.equal([
- {
- address: collectionIdAddress,
- event: 'Transfer',
- args: {
- from: '0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF',
- to: receiver,
- tokenId: tokenId.toString(),
- },
- },
- ]);
+ const events: any = [];
+ contract.events.allEvents((_: any, event: any) => {
+ events.push(event);
+ });
+ await tokenContract.methods.transfer(receiver, 1).send();
+
+ const event = events[0];
+ expect(event.address).to.equal(collectionAddress);
+ expect(event.returnValues.from).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');
+ expect(event.returnValues.to).to.equal(receiver);
+ expect(event.returnValues.tokenId).to.equal(tokenId.toString());
});
- itWeb3('transfer event on transfer from full ownership to partial ownership', async ({api, web3, privateKeyWrapper}) => {
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const receiver = createEthAccount(web3);
- const helper = evmCollectionHelpers(web3, caller);
- const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
- const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
- const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
+ itEth('transfer event on transfer from full ownership to partial ownership', async ({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+ const receiver = helper.eth.createAccount();
+ const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Transferry-Full-to-Partial', '6', '6');
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
const tokenId = await contract.methods.nextTokenId().call();
await contract.methods.mint(caller, tokenId).send();
- const tokenAddress = tokenIdToAddress(collectionId, tokenId);
- const tokenContract = uniqueRefungibleToken(web3, tokenAddress, caller);
+ const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);
await tokenContract.methods.repartition(2).send();
- const events = await recordEvents(contract, async () =>
- await tokenContract.methods.transfer(receiver, 1).send());
+ const events: any = [];
+ contract.events.allEvents((_: any, event: any) => {
+ events.push(event);
+ });
+ await tokenContract.methods.transfer(receiver, 1).send();
- expect(events).to.deep.equal([
- {
- address: collectionIdAddress,
- event: 'Transfer',
- args: {
- from: caller,
- to: '0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF',
- tokenId: tokenId.toString(),
- },
- },
- ]);
+ const event = events[0];
+ expect(event.address).to.equal(collectionAddress);
+ expect(event.returnValues.from).to.equal(caller);
+ expect(event.returnValues.to).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');
+ expect(event.returnValues.tokenId).to.equal(tokenId.toString());
});
});
describe('RFT: Fees', () => {
+ let donor: IKeyringPair;
+
before(async function() {
- await requirePallets(this, [Pallets.ReFungible]);
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+
+ donor = privateKey('//Alice');
+ });
});
- itWeb3('transferFrom() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const helper = evmCollectionHelpers(web3, caller);
- const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
- const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
- const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
-
- const receiver = createEthAccount(web3);
+ itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+ const receiver = helper.eth.createAccount();
+ const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Feeful-Transfer-From', '6', '6');
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
const tokenId = await contract.methods.nextTokenId().call();
await contract.methods.mint(caller, tokenId).send();
- const cost = await recordEthFee(api, caller, () => contract.methods.transferFrom(caller, receiver, tokenId).send());
- expect(cost < BigInt(0.2 * Number(UNIQUE)));
+ const cost = await helper.eth.recordCallFee(caller, () => contract.methods.transferFrom(caller, receiver, tokenId).send());
+ expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
expect(cost > 0n);
});
- itWeb3('transfer() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const helper = evmCollectionHelpers(web3, caller);
- const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
- const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
- const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
-
- const receiver = createEthAccount(web3);
+ itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+ const receiver = helper.eth.createAccount();
+ const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Feeful-Transfer', '6', '6');
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
const tokenId = await contract.methods.nextTokenId().call();
await contract.methods.mint(caller, tokenId).send();
- const cost = await recordEthFee(api, caller, () => contract.methods.transfer(receiver, tokenId).send());
- expect(cost < BigInt(0.2 * Number(UNIQUE)));
+ const cost = await helper.eth.recordCallFee(caller, () => contract.methods.transfer(receiver, tokenId).send());
+ expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
expect(cost > 0n);
});
});
describe('Common metadata', () => {
+ let donor: IKeyringPair;
+ let alice: IKeyringPair;
+
before(async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- });
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
- itWeb3('Returns collection name', async ({api, web3, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- name: 'token name',
- mode: {type: 'ReFungible'},
+ donor = privateKey('//Alice');
+ [alice] = await helper.arrange.createAccounts([20n], donor);
});
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ });
- const address = collectionIdToAddress(collection);
- const contract = evmCollection(web3, caller, address, {type: 'ReFungible'});
+ itEth('Returns collection name', async ({helper}) => {
+ const caller = helper.eth.createAccount();
+ const collection = await helper.rft.mintCollection(alice, {name: 'Leviathan', tokenPrefix: '11'});
+
+ const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'rft', caller);
const name = await contract.methods.name().call();
-
- expect(name).to.equal('token name');
+ expect(name).to.equal('Leviathan');
});
- itWeb3('Returns symbol name', async ({api, web3, privateKeyWrapper}) => {
- const collection = await createCollectionExpectSuccess({
- tokenPrefix: 'TOK',
- mode: {type: 'ReFungible'},
- });
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-
- const address = collectionIdToAddress(collection);
- const contract = evmCollection(web3, caller, address, {type: 'ReFungible'});
+ itEth('Returns symbol name', async ({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+ const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Leviathan', '', '12');
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
const symbol = await contract.methods.symbol().call();
-
- expect(symbol).to.equal('TOK');
+ expect(symbol).to.equal('12');
});
});
tests/src/eth/reFungibleAbi.jsondiffbeforeafterboth--- a/tests/src/eth/reFungibleAbi.json
+++ b/tests/src/eth/reFungibleAbi.json
@@ -91,27 +91,9 @@
},
{
"inputs": [
- { "internalType": "uint256", "name": "newAdmin", "type": "uint256" }
- ],
- "name": "addCollectionAdminSubstrate",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
{ "internalType": "address", "name": "user", "type": "address" }
],
"name": "addToCollectionAllowList",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "user", "type": "uint256" }
- ],
- "name": "addToCollectionAllowListSubstrate",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
@@ -273,15 +255,6 @@
{ "internalType": "address", "name": "user", "type": "address" }
],
"name": "isOwnerOrAdmin",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "user", "type": "uint256" }
- ],
- "name": "isOwnerOrAdminSubstrate",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "view",
"type": "function"
@@ -385,15 +358,6 @@
"type": "function"
},
{
- "inputs": [
- { "internalType": "uint256", "name": "admin", "type": "uint256" }
- ],
- "name": "removeCollectionAdminSubstrate",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
"inputs": [],
"name": "removeCollectionSponsor",
"outputs": [],
@@ -405,15 +369,6 @@
{ "internalType": "address", "name": "user", "type": "address" }
],
"name": "removeFromCollectionAllowList",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "user", "type": "uint256" }
- ],
- "name": "removeFromCollectionAllowListSubstrate",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
@@ -527,27 +482,9 @@
},
{
"inputs": [
- { "internalType": "uint256", "name": "sponsor", "type": "uint256" }
- ],
- "name": "setCollectionSponsorSubstrate",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
{ "internalType": "address", "name": "newOwner", "type": "address" }
],
"name": "setOwner",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "newOwner", "type": "uint256" }
- ],
- "name": "setOwnerSubstrate",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
tests/src/eth/reFungibleToken.test.tsdiffbeforeafterboth--- a/tests/src/eth/reFungibleToken.test.ts
+++ b/tests/src/eth/reFungibleToken.test.ts
@@ -14,82 +14,76 @@
// You should have received a copy of the GNU General Public License
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-import {approve, createCollection, createRefungibleToken, transfer, transferFrom, UNIQUE, requirePallets, Pallets} from '../util/helpers';
-import {collectionIdToAddress, createEthAccount, createEthAccountWithBalance, createRFTCollection, evmCollection, evmCollectionHelpers, getCollectionAddressFromResult, itWeb3, normalizeEvents, recordEthFee, recordEvents, subToEth, tokenIdToAddress, transferBalanceToEth, uniqueRefungible, uniqueRefungibleToken} from './util/helpers';
+import {Pallets, requirePalletsOrSkip} from '../util/playgrounds';
+import {EthUniqueHelper, expect, itEth, usingEthPlaygrounds} from './util/playgrounds';
+import {IKeyringPair} from '@polkadot/types/types';
+import {Contract} from 'web3-eth-contract';
-import chai from 'chai';
-import chaiAsPromised from 'chai-as-promised';
-chai.use(chaiAsPromised);
-const expect = chai.expect;
+describe('Refungible token: Information getting', () => {
+ let donor: IKeyringPair;
+ let alice: IKeyringPair;
-describe('Refungible token: Information getting', () => {
before(async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- });
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
- itWeb3('totalSupply', async ({api, web3, privateKeyWrapper}) => {
- const alice = privateKeyWrapper('//Alice');
-
- const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;
-
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ donor = privateKey('//Alice');
+ [alice] = await helper.arrange.createAccounts([20n], donor);
+ });
+ });
- const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: caller})).itemId;
+ itEth('totalSupply', async ({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+ const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'MUON'});
+ const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: caller});
- const address = tokenIdToAddress(collectionId, tokenId);
- const contract = uniqueRefungibleToken(web3, address, caller);
+ const contract = helper.ethNativeContract.rftTokenById(collection.collectionId, tokenId, caller);
const totalSupply = await contract.methods.totalSupply().call();
-
expect(totalSupply).to.equal('200');
});
- itWeb3('balanceOf', async ({api, web3, privateKeyWrapper}) => {
- const alice = privateKeyWrapper('//Alice');
-
- const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;
+ itEth('balanceOf', async ({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+ const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'MUON'});
+ const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: caller});
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-
- const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: caller})).itemId;
-
- const address = tokenIdToAddress(collectionId, tokenId);
- const contract = uniqueRefungibleToken(web3, address, caller);
+ const contract = helper.ethNativeContract.rftTokenById(collection.collectionId, tokenId, caller);
const balance = await contract.methods.balanceOf(caller).call();
-
expect(balance).to.equal('200');
});
- itWeb3('decimals', async ({api, web3, privateKeyWrapper}) => {
- const alice = privateKeyWrapper('//Alice');
+ itEth('decimals', async ({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+ const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'MUON'});
+ const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: caller});
- const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;
-
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-
- const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: caller})).itemId;
-
- const address = tokenIdToAddress(collectionId, tokenId);
- const contract = uniqueRefungibleToken(web3, address, caller);
+ const contract = helper.ethNativeContract.rftTokenById(collection.collectionId, tokenId, caller);
const decimals = await contract.methods.decimals().call();
-
expect(decimals).to.equal('0');
});
});
// FIXME: Need erc721 for ReFubgible.
describe('Check ERC721 token URI for ReFungible', () => {
+ let donor: IKeyringPair;
+
before(async function() {
- await requirePallets(this, [Pallets.ReFungible]);
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+
+ donor = privateKey('//Alice');
+ });
});
- itWeb3('Empty tokenURI', async ({web3, api, privateKeyWrapper}) => {
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const helper = evmCollectionHelpers(web3, owner);
- let result = await helper.methods.createERC721MetadataCompatibleCollection('Mint collection', '1', '1', '').send();
- const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
- const receiver = createEthAccount(web3);
- const contract = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'});
+ async function setup(helper: EthUniqueHelper, tokenPrefix: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const receiver = helper.eth.createAccount();
+ const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
+ let result = await collectionHelper.methods.createERC721MetadataCompatibleCollection('Mint collection', 'a', 'b', tokenPrefix).send();
+ const collectionAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+
const nextTokenId = await contract.methods.nextTokenId().call();
expect(nextTokenId).to.be.equal('1');
result = await contract.methods.mint(
@@ -97,166 +91,71 @@
nextTokenId,
).send();
- const events = normalizeEvents(result.events);
- const address = collectionIdToAddress(collectionId);
+ if (propertyKey && propertyValue) {
+ // Set URL or suffix
+ await contract.methods.setProperty(nextTokenId, propertyKey, Buffer.from(propertyValue)).send();
+ }
+
+ const event = result.events.Transfer;
+ expect(event.address).to.be.equal(collectionAddress);
+ expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
+ expect(event.returnValues.to).to.be.equal(receiver);
+ expect(event.returnValues.tokenId).to.be.equal(nextTokenId);
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Transfer',
- args: {
- from: '0x0000000000000000000000000000000000000000',
- to: receiver,
- tokenId: nextTokenId,
- },
- },
- ]);
+ return {contract, nextTokenId};
+ }
+ itEth('Empty tokenURI', async ({helper}) => {
+ const {contract, nextTokenId} = await setup(helper, '');
expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('');
});
-
- itWeb3('TokenURI from url', async ({web3, api, privateKeyWrapper}) => {
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const helper = evmCollectionHelpers(web3, owner);
- let result = await helper.methods.createERC721MetadataCompatibleCollection('Mint collection', '1', '1', 'BaseURI_').send();
- const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
- const receiver = createEthAccount(web3);
- const contract = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'});
- const nextTokenId = await contract.methods.nextTokenId().call();
- expect(nextTokenId).to.be.equal('1');
- result = await contract.methods.mint(
- receiver,
- nextTokenId,
- ).send();
-
- // Set URL
- await contract.methods.setProperty(nextTokenId, 'url', Buffer.from('Token URI')).send();
-
- const events = normalizeEvents(result.events);
- const address = collectionIdToAddress(collectionId);
-
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Transfer',
- args: {
- from: '0x0000000000000000000000000000000000000000',
- to: receiver,
- tokenId: nextTokenId,
- },
- },
- ]);
-
+ itEth('TokenURI from url', async ({helper}) => {
+ const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'url', 'Token URI');
expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI');
});
-
- itWeb3('TokenURI from baseURI + tokenId', async ({web3, api, privateKeyWrapper}) => {
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const helper = evmCollectionHelpers(web3, owner);
- let result = await helper.methods.createERC721MetadataCompatibleCollection('Mint collection', '1', '1', 'BaseURI_').send();
- const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
- const receiver = createEthAccount(web3);
- const contract = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'});
- const nextTokenId = await contract.methods.nextTokenId().call();
- expect(nextTokenId).to.be.equal('1');
- result = await contract.methods.mint(
- receiver,
- nextTokenId,
- ).send();
-
- const events = normalizeEvents(result.events);
- const address = collectionIdToAddress(collectionId);
-
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Transfer',
- args: {
- from: '0x0000000000000000000000000000000000000000',
- to: receiver,
- tokenId: nextTokenId,
- },
- },
- ]);
-
+ itEth('TokenURI from baseURI + tokenId', async ({helper}) => {
+ const {contract, nextTokenId} = await setup(helper, 'BaseURI_');
expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + nextTokenId);
});
- itWeb3('TokenURI from baseURI + suffix', async ({web3, api, privateKeyWrapper}) => {
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const helper = evmCollectionHelpers(web3, owner);
- let result = await helper.methods.createERC721MetadataCompatibleCollection('Mint collection', '1', '1', 'BaseURI_').send();
- const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
- const receiver = createEthAccount(web3);
- const contract = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'});
-
- const nextTokenId = await contract.methods.nextTokenId().call();
- expect(nextTokenId).to.be.equal('1');
- result = await contract.methods.mint(
- receiver,
- nextTokenId,
- ).send();
-
- // Set suffix
+ itEth('TokenURI from baseURI + suffix', async ({helper}) => {
const suffix = '/some/suffix';
- await contract.methods.setProperty(nextTokenId, 'suffix', Buffer.from(suffix)).send();
-
- const events = normalizeEvents(result.events);
- const address = collectionIdToAddress(collectionId);
-
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Transfer',
- args: {
- from: '0x0000000000000000000000000000000000000000',
- to: receiver,
- tokenId: nextTokenId,
- },
- },
- ]);
-
+ const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'suffix', suffix);
expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix);
});
});
describe('Refungible: Plain calls', () => {
+ let donor: IKeyringPair;
+ let alice: IKeyringPair;
+
before(async function() {
- await requirePallets(this, [Pallets.ReFungible]);
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+
+ donor = privateKey('//Alice');
+ [alice] = await helper.arrange.createAccounts([50n], donor);
+ });
});
- itWeb3('Can perform approve()', async ({web3, api, privateKeyWrapper}) => {
- const alice = privateKeyWrapper('//Alice');
+ itEth('Can perform approve()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const spender = helper.eth.createAccount();
+ const collection = await helper.rft.mintCollection(alice);
+ const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
- const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;
-
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-
- const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: owner})).itemId;
-
- const address = tokenIdToAddress(collectionId, tokenId);
+ const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+ const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);
- const spender = createEthAccount(web3);
-
- const contract = uniqueRefungibleToken(web3, address, owner);
-
{
const result = await contract.methods.approve(spender, 100).send({from: owner});
- const events = normalizeEvents(result.events);
-
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Approval',
- args: {
- owner,
- spender,
- value: '100',
- },
- },
- ]);
+ const event = result.events.Approval;
+ expect(event.address).to.be.equal(tokenAddress);
+ expect(event.returnValues.owner).to.be.equal(owner);
+ expect(event.returnValues.spender).to.be.equal(spender);
+ expect(event.returnValues.value).to.be.equal('100');
}
{
@@ -265,49 +164,31 @@
}
});
- itWeb3('Can perform transferFrom()', async ({web3, api, privateKeyWrapper}) => {
- const alice = privateKeyWrapper('//Alice');
-
- const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;
+ itEth('Can perform transferFrom()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const spender = await helper.eth.createAccountWithBalance(donor);
+ const receiver = helper.eth.createAccount();
+ const collection = await helper.rft.mintCollection(alice);
+ const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
- const owner = createEthAccount(web3);
- await transferBalanceToEth(api, alice, owner);
-
- const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: owner})).itemId;
-
- const spender = createEthAccount(web3);
- await transferBalanceToEth(api, alice, spender);
+ const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+ const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);
- const receiver = createEthAccount(web3);
-
- const address = tokenIdToAddress(collectionId, tokenId);
- const contract = uniqueRefungibleToken(web3, address, owner);
-
await contract.methods.approve(spender, 100).send();
{
const result = await contract.methods.transferFrom(owner, receiver, 49).send({from: spender});
- const events = normalizeEvents(result.events);
- expect(events).to.include.deep.members([
- {
- address,
- event: 'Transfer',
- args: {
- from: owner,
- to: receiver,
- value: '49',
- },
- },
- {
- address,
- event: 'Approval',
- args: {
- owner,
- spender,
- value: '51',
- },
- },
- ]);
+ let event = result.events.Transfer;
+ expect(event.address).to.be.equal(tokenAddress);
+ expect(event.returnValues.from).to.be.equal(owner);
+ expect(event.returnValues.to).to.be.equal(receiver);
+ expect(event.returnValues.value).to.be.equal('49');
+
+ event = result.events.Approval;
+ expect(event.address).to.be.equal(tokenAddress);
+ expect(event.returnValues.owner).to.be.equal(owner);
+ expect(event.returnValues.spender).to.be.equal(spender);
+ expect(event.returnValues.value).to.be.equal('51');
}
{
@@ -321,36 +202,22 @@
}
});
- itWeb3('Can perform transfer()', async ({web3, api, privateKeyWrapper}) => {
- const alice = privateKeyWrapper('//Alice');
+ itEth('Can perform transfer()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const receiver = helper.eth.createAccount();
+ const collection = await helper.rft.mintCollection(alice);
+ const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
- const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;
-
- const owner = createEthAccount(web3);
- await transferBalanceToEth(api, alice, owner);
-
- const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: owner})).itemId;
-
- const receiver = createEthAccount(web3);
- await transferBalanceToEth(api, alice, receiver);
-
- const address = tokenIdToAddress(collectionId, tokenId);
- const contract = uniqueRefungibleToken(web3, address, owner);
+ const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+ const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);
{
const result = await contract.methods.transfer(receiver, 50).send({from: owner});
- const events = normalizeEvents(result.events);
- expect(events).to.include.deep.members([
- {
- address,
- event: 'Transfer',
- args: {
- from: owner,
- to: receiver,
- value: '50',
- },
- },
- ]);
+ const event = result.events.Transfer;
+ expect(event.address).to.be.equal(tokenAddress);
+ expect(event.returnValues.from).to.be.equal(owner);
+ expect(event.returnValues.to).to.be.equal(receiver);
+ expect(event.returnValues.value).to.be.equal('50');
}
{
@@ -364,311 +231,262 @@
}
});
- itWeb3('Can perform repartition()', async ({web3, api, privateKeyWrapper}) => {
- const alice = privateKeyWrapper('//Alice');
-
- const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;
+ itEth('Can perform repartition()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const receiver = await helper.eth.createAccountWithBalance(donor);
+ const collection = await helper.rft.mintCollection(alice);
+ const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});
- const owner = createEthAccount(web3);
- await transferBalanceToEth(api, alice, owner);
+ const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+ const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);
- const receiver = createEthAccount(web3);
- await transferBalanceToEth(api, alice, receiver);
-
- const tokenId = (await createRefungibleToken(api, alice, collectionId, 100n, {Ethereum: owner})).itemId;
-
- const address = tokenIdToAddress(collectionId, tokenId);
- const contract = uniqueRefungibleToken(web3, address, owner);
-
await contract.methods.repartition(200).send({from: owner});
expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(200);
await contract.methods.transfer(receiver, 110).send({from: owner});
expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(90);
expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(110);
- await expect(contract.methods.repartition(80).send({from: owner})).to.eventually.be.rejected;
+ await expect(contract.methods.repartition(80).send({from: owner})).to.eventually.be.rejected; // Transaction is reverted
await contract.methods.transfer(receiver, 90).send({from: owner});
expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(0);
expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(200);
await contract.methods.repartition(150).send({from: receiver});
- await expect(contract.methods.transfer(owner, 160).send({from: receiver})).to.eventually.be.rejected;
+ await expect(contract.methods.transfer(owner, 160).send({from: receiver})).to.eventually.be.rejected; // Transaction is reverted
expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(150);
});
- itWeb3('Can repartition with increased amount', async ({web3, api, privateKeyWrapper}) => {
- const alice = privateKeyWrapper('//Alice');
-
- const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;
-
- const owner = createEthAccount(web3);
- await transferBalanceToEth(api, alice, owner);
-
- const tokenId = (await createRefungibleToken(api, alice, collectionId, 100n, {Ethereum: owner})).itemId;
+ itEth('Can repartition with increased amount', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const collection = await helper.rft.mintCollection(alice);
+ const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});
- const address = tokenIdToAddress(collectionId, tokenId);
- const contract = uniqueRefungibleToken(web3, address, owner);
+ const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+ const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);
const result = await contract.methods.repartition(200).send();
- const events = normalizeEvents(result.events);
- expect(events).to.deep.equal([
- {
- address,
- event: 'Transfer',
- args: {
- from: '0x0000000000000000000000000000000000000000',
- to: owner,
- value: '100',
- },
- },
- ]);
+ const event = result.events.Transfer;
+ expect(event.address).to.be.equal(tokenAddress);
+ expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
+ expect(event.returnValues.to).to.be.equal(owner);
+ expect(event.returnValues.value).to.be.equal('100');
});
- itWeb3('Can repartition with decreased amount', async ({web3, api, privateKeyWrapper}) => {
- const alice = privateKeyWrapper('//Alice');
+ itEth('Can repartition with decreased amount', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const collection = await helper.rft.mintCollection(alice);
+ const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});
- const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;
-
- const owner = createEthAccount(web3);
- await transferBalanceToEth(api, alice, owner);
-
- const tokenId = (await createRefungibleToken(api, alice, collectionId, 100n, {Ethereum: owner})).itemId;
-
- const address = tokenIdToAddress(collectionId, tokenId);
- const contract = uniqueRefungibleToken(web3, address, owner);
+ const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+ const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);
const result = await contract.methods.repartition(50).send();
- const events = normalizeEvents(result.events);
- expect(events).to.deep.equal([
- {
- address,
- event: 'Transfer',
- args: {
- from: owner,
- to: '0x0000000000000000000000000000000000000000',
- value: '50',
- },
- },
- ]);
+ const event = result.events.Transfer;
+ expect(event.address).to.be.equal(tokenAddress);
+ expect(event.returnValues.from).to.be.equal(owner);
+ expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000');
+ expect(event.returnValues.value).to.be.equal('50');
});
- itWeb3('Receiving Transfer event on burning into full ownership', async ({web3, api, privateKeyWrapper}) => {
- const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const receiver = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const helper = evmCollectionHelpers(web3, caller);
- const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
- const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
- const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
+ itEth('Receiving Transfer event on burning into full ownership', async ({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+ const receiver = await helper.eth.createAccountWithBalance(donor);
+ const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Devastation', '6', '6');
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
const tokenId = await contract.methods.nextTokenId().call();
await contract.methods.mint(caller, tokenId).send();
+ const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);
+ const tokenContract = helper.ethNativeContract.rftToken(tokenAddress, caller);
- const address = tokenIdToAddress(collectionId, tokenId);
-
- const tokenContract = uniqueRefungibleToken(web3, address, caller);
await tokenContract.methods.repartition(2).send();
await tokenContract.methods.transfer(receiver, 1).send();
- const events = await recordEvents(contract, async () =>
- await tokenContract.methods.burnFrom(caller, 1).send());
- expect(events).to.deep.equal([
- {
- address: collectionIdAddress,
- event: 'Transfer',
- args: {
- from: '0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF',
- to: receiver,
- tokenId,
- },
- },
- ]);
+ const events: any = [];
+ contract.events.allEvents((_: any, event: any) => {
+ events.push(event);
+ });
+ await tokenContract.methods.burnFrom(caller, 1).send();
+
+ const event = events[0];
+ expect(event.address).to.be.equal(collectionAddress);
+ expect(event.returnValues.from).to.be.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');
+ expect(event.returnValues.to).to.be.equal(receiver);
+ expect(event.returnValues.tokenId).to.be.equal(tokenId);
});
});
describe('Refungible: Fees', () => {
+ let donor: IKeyringPair;
+ let alice: IKeyringPair;
+
before(async function() {
- await requirePallets(this, [Pallets.ReFungible]);
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+
+ donor = privateKey('//Alice');
+ [alice] = await helper.arrange.createAccounts([50n], donor);
+ });
});
- itWeb3('approve() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {
- const alice = privateKeyWrapper('//Alice');
+ itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const spender = helper.eth.createAccount();
+ const collection = await helper.rft.mintCollection(alice);
+ const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});
- const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;
+ const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+ const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const spender = createEthAccount(web3);
-
- const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: owner})).itemId;
-
- const address = tokenIdToAddress(collectionId, tokenId);
- const contract = uniqueRefungibleToken(web3, address, owner);
-
- const cost = await recordEthFee(api, owner, () => contract.methods.approve(spender, 100).send({from: owner}));
- expect(cost < BigInt(0.2 * Number(UNIQUE)));
+ const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, 100).send({from: owner}));
+ expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
});
- itWeb3('transferFrom() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {
- const alice = privateKeyWrapper('//Alice');
-
- const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;
-
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const spender = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-
- const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: owner})).itemId;
+ itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const spender = await helper.eth.createAccountWithBalance(donor);
+ const collection = await helper.rft.mintCollection(alice);
+ const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
- const address = tokenIdToAddress(collectionId, tokenId);
- const contract = uniqueRefungibleToken(web3, address, owner);
+ const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+ const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);
await contract.methods.approve(spender, 100).send({from: owner});
- const cost = await recordEthFee(api, spender, () => contract.methods.transferFrom(owner, spender, 100).send({from: spender}));
- expect(cost < BigInt(0.2 * Number(UNIQUE)));
+ const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, 100).send({from: spender}));
+ expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
});
- itWeb3('transfer() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {
- const alice = privateKeyWrapper('//Alice');
+ itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const receiver = helper.eth.createAccount();
+ const collection = await helper.rft.mintCollection(alice);
+ const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
- const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;
+ const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+ const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const receiver = createEthAccount(web3);
-
- const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: owner})).itemId;
-
- const address = tokenIdToAddress(collectionId, tokenId);
- const contract = uniqueRefungibleToken(web3, address, owner);
-
- const cost = await recordEthFee(api, owner, () => contract.methods.transfer(receiver, 100).send({from: owner}));
- expect(cost < BigInt(0.2 * Number(UNIQUE)));
+ const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, 100).send({from: owner}));
+ expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
});
});
describe('Refungible: Substrate calls', () => {
+ let donor: IKeyringPair;
+ let alice: IKeyringPair;
+
before(async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- });
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
- itWeb3('Events emitted for approve()', async ({web3, api, privateKeyWrapper}) => {
- const alice = privateKeyWrapper('//Alice');
-
- const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;
-
- const receiver = createEthAccount(web3);
+ donor = privateKey('//Alice');
+ [alice] = await helper.arrange.createAccounts([50n], donor);
+ });
+ });
- const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n)).itemId;
+ itEth('Events emitted for approve()', async ({helper}) => {
+ const receiver = helper.eth.createAccount();
+ const collection = await helper.rft.mintCollection(alice);
+ const token = await collection.mintToken(alice, 200n);
- const address = tokenIdToAddress(collectionId, tokenId);
- const contract = uniqueRefungibleToken(web3, address);
+ const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);
+ const contract = helper.ethNativeContract.rftToken(tokenAddress);
- const events = await recordEvents(contract, async () => {
- expect(await approve(api, collectionId, tokenId, alice, {Ethereum: receiver}, 100n)).to.be.true;
+ const events: any = [];
+ contract.events.allEvents((_: any, event: any) => {
+ events.push(event);
});
+ expect(await token.approve(alice, {Ethereum: receiver}, 100n)).to.be.true;
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Approval',
- args: {
- owner: subToEth(alice.address),
- spender: receiver,
- value: '100',
- },
- },
- ]);
+ const event = events[0];
+ expect(event.event).to.be.equal('Approval');
+ expect(event.address).to.be.equal(tokenAddress);
+ expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));
+ expect(event.returnValues.spender).to.be.equal(receiver);
+ expect(event.returnValues.value).to.be.equal('100');
});
- itWeb3('Events emitted for transferFrom()', async ({web3, api, privateKeyWrapper}) => {
- const alice = privateKeyWrapper('//Alice');
+ itEth('Events emitted for transferFrom()', async ({helper}) => {
+ const [bob] = await helper.arrange.createAccounts([10n], donor);
+ const receiver = helper.eth.createAccount();
+ const collection = await helper.rft.mintCollection(alice);
+ const token = await collection.mintToken(alice, 200n);
+ await token.approve(alice, {Substrate: bob.address}, 100n);
- const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;
- const bob = privateKeyWrapper('//Bob');
+ const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);
+ const contract = helper.ethNativeContract.rftToken(tokenAddress);
- const receiver = createEthAccount(web3);
+ const events: any = [];
+ contract.events.allEvents((_: any, event: any) => {
+ events.push(event);
+ });
- const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n)).itemId;
- expect(await approve(api, collectionId, tokenId, alice, bob.address, 100n)).to.be.true;
+ expect(await token.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver}, 51n)).to.be.true;
- const address = tokenIdToAddress(collectionId, tokenId);
- const contract = uniqueRefungibleToken(web3, address);
+ let event = events[0];
+ expect(event.event).to.be.equal('Transfer');
+ expect(event.address).to.be.equal(tokenAddress);
+ expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
+ expect(event.returnValues.to).to.be.equal(receiver);
+ expect(event.returnValues.value).to.be.equal('51');
- const events = await recordEvents(contract, async () => {
- expect(await transferFrom(api, collectionId, tokenId, bob, alice, {Ethereum: receiver}, 51n)).to.be.true;
- });
-
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Transfer',
- args: {
- from: subToEth(alice.address),
- to: receiver,
- value: '51',
- },
- },
- {
- address,
- event: 'Approval',
- args: {
- owner: subToEth(alice.address),
- spender: subToEth(bob.address),
- value: '49',
- },
- },
- ]);
+ event = events[1];
+ expect(event.event).to.be.equal('Approval');
+ expect(event.address).to.be.equal(tokenAddress);
+ expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));
+ expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(bob.address));
+ expect(event.returnValues.value).to.be.equal('49');
});
- itWeb3('Events emitted for transfer()', async ({web3, api, privateKeyWrapper}) => {
- const alice = privateKeyWrapper('//Alice');
+ itEth('Events emitted for transfer()', async ({helper}) => {
+ const receiver = helper.eth.createAccount();
+ const collection = await helper.rft.mintCollection(alice);
+ const token = await collection.mintToken(alice, 200n);
- const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;
+ const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);
+ const contract = helper.ethNativeContract.rftToken(tokenAddress);
- const receiver = createEthAccount(web3);
-
- const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n)).itemId;
+ const events: any = [];
+ contract.events.allEvents((_: any, event: any) => {
+ events.push(event);
+ });
- const address = tokenIdToAddress(collectionId, tokenId);
- const contract = uniqueRefungibleToken(web3, address);
+ expect(await token.transfer(alice, {Ethereum: receiver}, 51n)).to.be.true;
- const events = await recordEvents(contract, async () => {
- expect(await transfer(api, collectionId, tokenId, alice, {Ethereum: receiver}, 51n)).to.be.true;
- });
-
- expect(events).to.be.deep.equal([
- {
- address,
- event: 'Transfer',
- args: {
- from: subToEth(alice.address),
- to: receiver,
- value: '51',
- },
- },
- ]);
+ const event = events[0];
+ expect(event.event).to.be.equal('Transfer');
+ expect(event.address).to.be.equal(tokenAddress);
+ expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
+ expect(event.returnValues.to).to.be.equal(receiver);
+ expect(event.returnValues.value).to.be.equal('51');
});
});
describe('ERC 1633 implementation', () => {
+ let donor: IKeyringPair;
+
before(async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- });
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
- itWeb3('Default parent token address and id', async ({api, web3, privateKeyWrapper}) => {
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ donor = privateKey('//Alice');
+ });
+ });
- const {collectionIdAddress, collectionId} = await createRFTCollection(api, web3, owner);
- const refungibleContract = uniqueRefungible(web3, collectionIdAddress, owner);
- const refungibleTokenId = await refungibleContract.methods.nextTokenId().call();
- await refungibleContract.methods.mint(owner, refungibleTokenId).send();
+ itEth('Default parent token address and id', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
- const rftTokenAddress = tokenIdToAddress(collectionId, refungibleTokenId);
- const refungibleTokenContract = uniqueRefungibleToken(web3, rftTokenAddress, owner);
+ const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Sands', '', 'GRAIN');
+ const collectionContract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+
+ const tokenId = await collectionContract.methods.nextTokenId().call();
+ await collectionContract.methods.mint(owner, tokenId).send();
+ const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);
+ const tokenContract = helper.ethNativeContract.rftToken(tokenAddress, owner);
- const tokenAddress = await refungibleTokenContract.methods.parentToken().call();
- const tokenId = await refungibleTokenContract.methods.parentTokenId().call();
- expect(tokenAddress).to.be.equal(collectionIdAddress);
- expect(tokenId).to.be.equal(refungibleTokenId);
+ expect(await tokenContract.methods.parentToken().call()).to.be.equal(collectionAddress);
+ expect(await tokenContract.methods.parentTokenId().call()).to.be.equal(tokenId);
});
});
tests/src/eth/util/playgrounds/index.tsdiffbeforeafterboth--- a/tests/src/eth/util/playgrounds/index.ts
+++ b/tests/src/eth/util/playgrounds/index.ts
@@ -12,6 +12,7 @@
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
+import {requirePalletsOrSkip} from '../../../util/playgrounds';
chai.use(chaiAsPromised);
export const expect = chai.expect;
@@ -35,15 +36,26 @@
}
};
-export async function itEth(name: string, cb: (apis: { helper: EthUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any, opts: { only?: boolean, skip?: boolean } = {}) {
- let i: any = it;
- if (opts.only) i = i.only;
- else if (opts.skip) i = i.skip;
- i(name, async () => {
+export async function itEth(name: string, cb: (apis: { helper: EthUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) {
+ (opts.only ? it.only :
+ opts.skip ? it.skip : it)(name, async function() {
await usingEthPlaygrounds(async (helper, privateKey) => {
+ if (opts.requiredPallets) {
+ requirePalletsOrSkip(this, helper, opts.requiredPallets);
+ }
+
await cb({helper, privateKey});
});
});
}
+
+export async function itEthIfWithPallet(name: string, required: string[], cb: (apis: { helper: EthUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) {
+ return itEth(name, cb, {requiredPallets: required, ...opts});
+}
+
itEth.only = (name: string, cb: (apis: { helper: EthUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any) => itEth(name, cb, {only: true});
-itEth.skip = (name: string, cb: (apis: { helper: EthUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any) => itEth(name, cb, {skip: true});
\ No newline at end of file
+itEth.skip = (name: string, cb: (apis: { helper: EthUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any) => itEth(name, cb, {skip: true});
+
+itEthIfWithPallet.only = (name: string, required: string[], cb: (apis: { helper: EthUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any) => itEthIfWithPallet(name, required, cb, {only: true});
+itEthIfWithPallet.skip = (name: string, required: string[], cb: (apis: { helper: EthUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any) => itEthIfWithPallet(name, required, cb, {skip: true});
+itEth.ifWithPallets = itEthIfWithPallet;
tests/src/eth/util/playgrounds/unique.dev.tsdiffbeforeafterboth--- a/tests/src/eth/util/playgrounds/unique.dev.ts
+++ b/tests/src/eth/util/playgrounds/unique.dev.ts
@@ -27,6 +27,7 @@
import refungibleAbi from '../../reFungibleAbi.json';
import refungibleTokenAbi from '../../reFungibleTokenAbi.json';
import contractHelpersAbi from './../contractHelpersAbi.json';
+import {TEthereumAccount} from '../../../util/playgrounds/types';
class EthGroupBase {
helper: EthUniqueHelper;
@@ -43,13 +44,13 @@
return {error: `File not found: ${path}`};
};
- const knownImports = {} as any;
+ const knownImports = {} as {[key: string]: string};
for(const imp of imports) {
knownImports[imp.solPath] = (await readFile(imp.fsPath)).toString();
}
return function(path: string) {
- if(knownImports.hasOwnPropertyDescriptor(path)) return {contents: knownImports[path]};
+ if(path in knownImports) return {contents: knownImports[path]};
return {error: `File not found: ${path}`};
};
}
@@ -116,13 +117,17 @@
return new web3.eth.Contract(abi as any, address, {gas: this.helper.eth.DEFAULT_GAS, ...(caller ? {from: caller} : {})});
}
- rftTokenByAddress(address: string, caller?: string): Contract {
+ collectionById(collectionId: number, mode: 'nft' | 'rft' | 'ft', caller?: string): Contract {
+ return this.collection(this.helper.ethAddress.fromCollectionId(collectionId), mode, caller);
+ }
+
+ rftToken(address: string, caller?: string): Contract {
const web3 = this.helper.getWeb3();
return new web3.eth.Contract(refungibleTokenAbi as any, address, {gas: this.helper.eth.DEFAULT_GAS, ...(caller ? {from: caller} : {})});
}
- rftToken(collectionId: number, tokenId: number, caller?: string): Contract {
- return this.rftTokenByAddress(this.helper.ethAddress.fromTokenId(collectionId, tokenId), caller);
+ rftTokenById(collectionId: number, tokenId: number, caller?: string): Contract {
+ return this.rftToken(this.helper.ethAddress.fromTokenId(collectionId, tokenId), caller);
}
}
@@ -148,7 +153,7 @@
return await this.helper.balance.transferToSubstrate(donor, evmToAddress(recepient), amount * (inTokens ? this.helper.balance.getOneTokenNominal() : 1n));
}
- async callEVM(signer: IKeyringPair, contractAddress: string, abi: any, value: string, gasLimit?: number) {
+ async sendEVM(signer: IKeyringPair, contractAddress: string, abi: string, value: string, gasLimit?: number) {
if(!gasLimit) gasLimit = this.DEFAULT_GAS;
const web3 = this.helper.getWeb3();
const gasPrice = await web3.eth.getGasPrice();
@@ -159,6 +164,10 @@
true,
);
}
+
+ async callEVM(signer: TEthereumAccount, contractAddress: string, abi: string) {
+ return await this.helper.callRpc('api.rpc.eth.call', [{from: signer, to: contractAddress, data: abi}]);
+ }
async createNonfungibleCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{collectionId: number, collectionAddress: string}> {
const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer);
@@ -171,6 +180,17 @@
return {collectionId, collectionAddress};
}
+ async createRefungibleCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{collectionId: number, collectionAddress: string}> {
+ const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer);
+
+ const result = await collectionHelper.methods.createRFTCollection(name, description, tokenPrefix).send();
+
+ const collectionAddress = this.helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
+ const collectionId = this.helper.ethAddress.extractCollectionId(collectionAddress);
+
+ return {collectionId, collectionAddress};
+ }
+
async deployCollectorContract(signer: string): Promise<Contract> {
return await this.helper.ethContract.deployByCode(signer, 'Collector', `
// SPDX-License-Identifier: UNLICENSED
@@ -215,6 +235,16 @@
}
`);
}
+
+ async recordCallFee(user: string, call: () => Promise<any>): Promise<bigint> {
+ const before = await this.helper.balance.getEthereum(user);
+ await call();
+ // In dev mode, the transaction might not finish processing in time
+ await this.helper.wait.newBlocks(1);
+ const after = await this.helper.balance.getEthereum(user);
+
+ return before - after;
+ }
}
class EthAddressGroup extends EthGroupBase {
@@ -240,7 +270,7 @@
}
fromTokenId(collectionId: number, tokenId: number): string {
- return this.helper.util.getNestingTokenAddress(collectionId, tokenId);
+ return this.helper.util.getTokenAddress({collectionId, tokenId});
}
normalizeAddress(address: string): string {
tests/src/nesting/graphs.test.tsdiffbeforeafterboth--- a/tests/src/nesting/graphs.test.ts
+++ b/tests/src/nesting/graphs.test.ts
@@ -1,9 +1,22 @@
-import {ApiPromise} from '@polkadot/api';
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
import {IKeyringPair} from '@polkadot/types/types';
-import {expect} from 'chai';
-import {tokenIdToCross} from '../eth/util/helpers';
-import usingApi, {executeTransaction} from '../substrate/substrate-api';
-import {getCreateCollectionResult, transferExpectSuccess, setCollectionLimitsExpectSuccess} from '../util/helpers';
+import {expect, itSub, usingPlaygrounds} from '../util/playgrounds';
+import {UniqueHelper, UniqueNFToken} from '../util/playgrounds/unique';
/**
* ```dot
@@ -12,46 +25,47 @@
* 8 -> 5
* ```
*/
-async function buildComplexObjectGraph(api: ApiPromise, sender: IKeyringPair): Promise<number> {
- const events = await executeTransaction(api, sender, api.tx.unique.createCollectionEx({mode: 'NFT', permissions: {nesting: {tokenOwner: true}}}));
- const {collectionId} = getCreateCollectionResult(events);
+async function buildComplexObjectGraph(helper: UniqueHelper, sender: IKeyringPair): Promise<UniqueNFToken[]> {
+ const collection = await helper.nft.mintCollection(sender, {permissions: {nesting: {tokenOwner: true}}});
+ const tokens = await collection.mintMultipleTokens(sender, Array(8).fill({owner: {Substrate: sender.address}}));
- await executeTransaction(api, sender, api.tx.unique.createMultipleItemsEx(collectionId, {NFT: Array(8).fill({owner: {Substrate: sender.address}})}));
-
- await transferExpectSuccess(collectionId, 8, sender, tokenIdToCross(collectionId, 5));
-
- await transferExpectSuccess(collectionId, 7, sender, tokenIdToCross(collectionId, 6));
- await transferExpectSuccess(collectionId, 6, sender, tokenIdToCross(collectionId, 5));
- await transferExpectSuccess(collectionId, 5, sender, tokenIdToCross(collectionId, 2));
+ await tokens[7].nest(sender, tokens[4]);
+ await tokens[6].nest(sender, tokens[5]);
+ await tokens[5].nest(sender, tokens[4]);
+ await tokens[4].nest(sender, tokens[1]);
+ await tokens[3].nest(sender, tokens[2]);
+ await tokens[2].nest(sender, tokens[1]);
+ await tokens[1].nest(sender, tokens[0]);
- await transferExpectSuccess(collectionId, 4, sender, tokenIdToCross(collectionId, 3));
- await transferExpectSuccess(collectionId, 3, sender, tokenIdToCross(collectionId, 2));
- await transferExpectSuccess(collectionId, 2, sender, tokenIdToCross(collectionId, 1));
-
- return collectionId;
+ return tokens;
}
describe('Graphs', () => {
- it('Ouroboros can\'t be created in a complex graph', async () => {
- await usingApi(async (api, privateKeyWrapper) => {
- const alice = privateKeyWrapper('//Alice');
- const collection = await buildComplexObjectGraph(api, alice);
- const tokenTwoParent = tokenIdToCross(collection, 1);
+ let alice: IKeyringPair;
- // to self
- await expect(
- executeTransaction(api, alice, api.tx.unique.transfer(tokenIdToCross(collection, 1), collection, 1, 1)),
- 'first transaction',
- ).to.be.rejectedWith(/structure\.OuroborosDetected/);
- // to nested part of graph
- await expect(
- executeTransaction(api, alice, api.tx.unique.transfer(tokenIdToCross(collection, 5), collection, 1, 1)),
- 'second transaction',
- ).to.be.rejectedWith(/structure\.OuroborosDetected/);
- await expect(
- executeTransaction(api, alice, api.tx.unique.transferFrom(tokenTwoParent, tokenIdToCross(collection, 8), collection, 2, 1)),
- 'third transaction',
- ).to.be.rejectedWith(/structure\.OuroborosDetected/);
+ before(async () => {
+ await usingPlaygrounds(async (helper, privateKey) => {
+ const donor = privateKey('//Alice');
+ [alice] = await helper.arrange.createAccounts([10n], donor);
});
});
+
+ itSub('Ouroboros can\'t be created in a complex graph', async ({helper}) => {
+ const tokens = await buildComplexObjectGraph(helper, alice);
+
+ // to self
+ await expect(
+ tokens[0].nest(alice, tokens[0]),
+ 'first transaction',
+ ).to.be.rejectedWith(/structure\.OuroborosDetected/);
+ // to nested part of graph
+ await expect(
+ tokens[0].nest(alice, tokens[4]),
+ 'second transaction',
+ ).to.be.rejectedWith(/structure\.OuroborosDetected/);
+ await expect(
+ tokens[1].transferFrom(alice, tokens[0].nestingAccount(), tokens[7].nestingAccount()),
+ 'third transaction',
+ ).to.be.rejectedWith(/structure\.OuroborosDetected/);
+ });
});
tests/src/nesting/migration-check.test.tsdiffbeforeafterboth--- a/tests/src/nesting/migration-check.test.ts
+++ b/tests/src/nesting/migration-check.test.ts
@@ -8,7 +8,8 @@
import find from 'find-process';
// todo un-skip for migrations
-describe.skip('Migration testing', () => {
+// todo:playgrounds skipped, this one is outdated. Probably to be deleted/replaced.
+describe.skip('Migration testing: Properties', () => {
let alice: IKeyringPair;
before(async() => {
tests/src/nesting/nest.test.tsdiffbeforeafterboth--- a/tests/src/nesting/nest.test.ts
+++ b/tests/src/nesting/nest.test.ts
@@ -1,840 +1,667 @@
-import {expect} from 'chai';
-import {tokenIdToAddress} from '../eth/util/helpers';
-import usingApi, {executeTransaction} from '../substrate/substrate-api';
-import {
- addCollectionAdminExpectSuccess,
- addToAllowListExpectSuccess,
- createCollectionExpectSuccess,
- createItemExpectSuccess,
- enableAllowListExpectSuccess,
- enablePublicMintingExpectSuccess,
- getTokenChildren,
- getTokenOwner,
- getTopmostTokenOwner,
- normalizeAccountId,
- setCollectionPermissionsExpectSuccess,
- transferExpectFailure,
- transferExpectSuccess,
- transferFromExpectSuccess,
- setCollectionLimitsExpectSuccess,
- requirePallets,
- Pallets,
-} from '../util/helpers';
-import {IKeyringPair} from '@polkadot/types/types';
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
-let alice: IKeyringPair;
-let bob: IKeyringPair;
-let charlie: IKeyringPair;
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+import {IKeyringPair} from '@polkadot/types/types';
+import {expect, itSub, Pallets, usingPlaygrounds} from '../util/playgrounds';
+
describe('Integration Test: Composite nesting tests', () => {
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
+
before(async () => {
- await usingApi(async (_, privateKeyWrapper) => {
- alice = privateKeyWrapper('//Alice');
- bob = privateKeyWrapper('//Bob');
+ await usingPlaygrounds(async (helper, privateKey) => {
+ const donor = privateKey('//Alice');
+ [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor);
});
});
- it('Performs the full suite: bundles a token, transfers, and unnests', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
+ itSub('Performs the full suite: bundles a token, transfers, and unnests', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+ const targetToken = await collection.mintToken(alice);
- // Create a nested token
- const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});
- expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: alice.address});
- expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});
+ // Create an immediately nested token
+ const nestedToken = await collection.mintToken(alice, targetToken.nestingAccount());
+ expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address});
+ expect(await nestedToken.getOwner()).to.be.deep.equal(targetToken.nestingAccount().toLowerCase());
+
+ // Create a token to be nested
+ const newToken = await collection.mintToken(alice);
- // Create a token to be nested
- const newToken = await createItemExpectSuccess(alice, collection, 'NFT');
-
- // Nest
- await transferExpectSuccess(collection, newToken, alice, {Ethereum: tokenIdToAddress(collection, targetToken)});
- expect(await getTopmostTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});
- expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});
+ // Nest
+ await newToken.nest(alice, targetToken);
+ expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address});
+ expect(await newToken.getOwner()).to.be.deep.equal(targetToken.nestingAccount().toLowerCase());
- // Move bundle to different user
- await transferExpectSuccess(collection, targetToken, alice, {Substrate: bob.address});
- expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: bob.address});
- expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});
+ // Move bundle to different user
+ await targetToken.transfer(alice, {Substrate: bob.address});
+ expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address});
+ expect(await nestedToken.getOwner()).to.be.deep.equal(targetToken.nestingAccount().toLowerCase());
+ expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address});
+ expect(await newToken.getOwner()).to.be.deep.equal(targetToken.nestingAccount().toLowerCase());
- // Unnest
- await transferFromExpectSuccess(collection, newToken, bob, {Ethereum: tokenIdToAddress(collection, targetToken)}, {Substrate: bob.address});
- expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: bob.address});
- });
+ // Unnest
+ await newToken.unnest(bob, targetToken, {Substrate: bob.address});
+ expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address});
+ expect(await newToken.getOwner()).to.be.deep.equal({Substrate: bob.address});
});
-
- it('Transfers an already bundled token', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
- const tokenA = await createItemExpectSuccess(alice, collection, 'NFT');
- const tokenB = await createItemExpectSuccess(alice, collection, 'NFT');
-
- // Create a nested token
- const tokenC = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, tokenA)});
- expect(await getTopmostTokenOwner(api, collection, tokenC)).to.be.deep.equal({Substrate: alice.address});
- expect(await getTokenOwner(api, collection, tokenC)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, tokenA).toLowerCase()});
+ itSub('Transfers an already bundled token', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+ const tokenA = await collection.mintToken(alice);
+ const tokenB = await collection.mintToken(alice);
- // Transfer the nested token to another token
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.transferFrom(
- normalizeAccountId({Ethereum: tokenIdToAddress(collection, tokenA)}),
- normalizeAccountId({Ethereum: tokenIdToAddress(collection, tokenB)}),
- collection,
- tokenC,
- 1,
- ),
- )).to.not.be.rejected;
- expect(await getTopmostTokenOwner(api, collection, tokenC)).to.be.deep.equal({Substrate: alice.address});
- expect(await getTokenOwner(api, collection, tokenC)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, tokenB).toLowerCase()});
- });
+ // Create a nested token
+ const tokenC = await collection.mintToken(alice, tokenA.nestingAccount());
+ expect(await tokenC.getOwner()).to.be.deep.equal(tokenA.nestingAccount().toLowerCase());
+
+ // Transfer the nested token to another token
+ await expect(tokenC.transferFrom(alice, tokenA.nestingAccount(), tokenB.nestingAccount())).to.be.fulfilled;
+ expect(await tokenC.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address});
+ expect(await tokenC.getOwner()).to.be.deep.equal(tokenB.nestingAccount().toLowerCase());
});
- it('Checks token children', async () => {
- await usingApi(async api => {
- const collectionA = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionLimitsExpectSuccess(alice, collectionA, {ownerCanTransfer: true});
- await setCollectionPermissionsExpectSuccess(alice, collectionA, {nesting: {tokenOwner: true}});
- const collectionB = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ itSub('Checks token children', async ({helper}) => {
+ const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+ const collectionB = await helper.ft.mintCollection(alice);
+
+ const targetToken = await collectionA.mintToken(alice);
+ expect((await targetToken.getChildren()).length).to.be.equal(0, 'Children length check at creation');
- const targetToken = await createItemExpectSuccess(alice, collectionA, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collectionA, targetToken)};
- let children = await getTokenChildren(api, collectionA, targetToken);
- expect(children.length).to.be.equal(0, 'Children length check at creation');
+ // Create a nested NFT token
+ const tokenA = await collectionA.mintToken(alice, targetToken.nestingAccount());
+ expect(await targetToken.getChildren()).to.have.deep.members([
+ {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
+ ], 'Children contents check at nesting #1').and.be.length(1, 'Children length check at nesting #1');
- // Create a nested NFT token
- const tokenA = await createItemExpectSuccess(alice, collectionA, 'NFT', targetAddress);
- children = await getTokenChildren(api, collectionA, targetToken);
- expect(children.length).to.be.equal(1, 'Children length check at nesting #1');
- expect(children).to.have.deep.members([
- {token: tokenA, collection: collectionA},
- ], 'Children contents check at nesting #1');
+ // Create then nest
+ const tokenB = await collectionA.mintToken(alice);
+ await tokenB.nest(alice, targetToken);
+ expect(await targetToken.getChildren()).to.have.deep.members([
+ {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
+ {tokenId: tokenB.tokenId, collectionId: collectionA.collectionId},
+ ], 'Children contents check at nesting #2').and.be.length(2, 'Children length check at nesting #2');
- // Create then nest
- const tokenB = await createItemExpectSuccess(alice, collectionA, 'NFT');
- await transferExpectSuccess(collectionA, tokenB, alice, targetAddress);
- children = await getTokenChildren(api, collectionA, targetToken);
- expect(children.length).to.be.equal(2, 'Children length check at nesting #2');
- expect(children).to.have.deep.members([
- {token: tokenA, collection: collectionA},
- {token: tokenB, collection: collectionA},
- ], 'Children contents check at nesting #2');
+ // Move token B to a different user outside the nesting tree
+ await tokenB.unnest(alice, targetToken, {Substrate: bob.address});
+ expect(await targetToken.getChildren()).to.be.have.deep.members([
+ {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
+ ], 'Children contents check at nesting #3 (unnesting)').and.be.length(1, 'Children length check at nesting #3 (unnesting)');
- // Move token B to a different user outside the nesting tree
- await transferFromExpectSuccess(collectionA, tokenB, alice, targetAddress, bob);
- children = await getTokenChildren(api, collectionA, targetToken);
- expect(children.length).to.be.equal(1, 'Children length check at unnesting');
- expect(children).to.be.have.deep.members([
- {token: tokenA, collection: collectionA},
- ], 'Children contents check at unnesting');
+ // Create a fungible token in another collection and then nest
+ await collectionB.mint(alice, 10n);
+ await collectionB.transfer(alice, targetToken.nestingAccount(), 2n);
+ expect(await targetToken.getChildren()).to.be.have.deep.members([
+ {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
+ {tokenId: 0, collectionId: collectionB.collectionId},
+ ], 'Children contents check at nesting #4 (from another collection)')
+ .and.be.length(2, 'Children length check at nesting #4 (from another collection)');
+
+ // Move part of the fungible token inside token A deeper in the nesting tree
+ await collectionB.transferFrom(alice, targetToken.nestingAccount(), tokenA.nestingAccount(), 1n);
+ expect(await targetToken.getChildren()).to.be.have.deep.members([
+ {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
+ {tokenId: 0, collectionId: collectionB.collectionId},
+ ], 'Children contents check at nesting #5 (deeper)').and.be.length(2, 'Children length check at nesting #5 (deeper)');
+ expect(await tokenA.getChildren()).to.be.have.deep.members([
+ {tokenId: 0, collectionId: collectionB.collectionId},
+ ], 'Children contents check at nesting #5.5 (deeper)').and.be.length(1, 'Children length check at nesting #5.5 (deeper)');
- // Create a fungible token in another collection and then nest
- const tokenC = await createItemExpectSuccess(alice, collectionB, 'Fungible');
- await transferExpectSuccess(collectionB, tokenC, alice, targetAddress, 1, 'Fungible');
- children = await getTokenChildren(api, collectionA, targetToken);
- expect(children.length).to.be.equal(2, 'Children length check at nesting #3 (from another collection)');
- expect(children).to.be.have.deep.members([
- {token: tokenA, collection: collectionA},
- {token: tokenC, collection: collectionB},
- ], 'Children contents check at nesting #3 (from another collection)');
-
- // Move the fungible token inside token A deeper in the nesting tree
- await transferFromExpectSuccess(collectionB, tokenC, alice, targetAddress, {Ethereum: tokenIdToAddress(collectionA, tokenA)}, 1, 'Fungible');
- children = await getTokenChildren(api, collectionA, targetToken);
- expect(children.length).to.be.equal(1, 'Children length check at deeper nesting');
- expect(children).to.be.have.deep.members([
- {token: tokenA, collection: collectionA},
- ], 'Children contents check at deeper nesting');
- });
+ // Move the remaining part of the fungible token inside token A deeper in the nesting tree
+ await collectionB.transferFrom(alice, targetToken.nestingAccount(), tokenA.nestingAccount(), 1n);
+ expect(await targetToken.getChildren()).to.be.have.deep.members([
+ {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
+ ], 'Children contents check at nesting #6 (deeper)').and.be.length(1, 'Children length check at nesting #6 (deeper)');
+ expect(await tokenA.getChildren()).to.be.have.deep.members([
+ {tokenId: 0, collectionId: collectionB.collectionId},
+ ], 'Children contents check at nesting #6.5 (deeper)').and.be.length(1, 'Children length check at nesting #6.5 (deeper)');
});
});
-describe('Integration Test: Various token type nesting', async () => {
+describe('Integration Test: Various token type nesting', () => {
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
+ let charlie: IKeyringPair;
+
before(async () => {
- await usingApi(async (_, privateKeyWrapper) => {
- alice = privateKeyWrapper('//Alice');
- bob = privateKeyWrapper('//Bob');
- charlie = privateKeyWrapper('//Charlie');
+ await usingPlaygrounds(async (helper, privateKey) => {
+ const donor = privateKey('//Alice');
+ [alice, bob, charlie] = await helper.arrange.createAccounts([50n, 10n, 10n], donor);
});
});
- it('Admin (NFT): allows an Admin to nest a token', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {collectionAdmin: true}});
- await addCollectionAdminExpectSuccess(alice, collection, bob.address);
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT', charlie.address);
+ itSub('Admin (NFT): allows an Admin to nest a token', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {collectionAdmin: true}}});
+ await collection.addAdmin(alice, {Substrate: bob.address});
+ const targetToken = await collection.mintToken(alice, {Substrate: charlie.address});
- // Create a nested token
- const nestedToken = await createItemExpectSuccess(bob, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});
- expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: charlie.address});
- expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});
+ // Create an immediately nested token
+ const nestedToken = await collection.mintToken(bob, targetToken.nestingAccount());
+ expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
+ expect(await nestedToken.getOwner()).to.be.deep.equal(targetToken.nestingAccount().toLowerCase());
- // Create a token to be nested and nest
- const newToken = await createItemExpectSuccess(bob, collection, 'NFT');
- await transferExpectSuccess(collection, newToken, bob, {Ethereum: tokenIdToAddress(collection, targetToken)});
- expect(await getTopmostTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: charlie.address});
- expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});
- });
+ // Create a token to be nested and nest
+ const newToken = await collection.mintToken(bob);
+ await newToken.nest(bob, targetToken);
+ expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
+ expect(await newToken.getOwner()).to.be.deep.equal(targetToken.nestingAccount().toLowerCase());
});
- it('Admin (NFT): Admin and Token Owner can operate together', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true, collectionAdmin: true}});
- await addCollectionAdminExpectSuccess(alice, collection, bob.address);
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT', charlie.address);
+ itSub('Admin (NFT): Admin and Token Owner can operate together', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {collectionAdmin: true, tokenOwner: true}}});
+ await collection.addAdmin(alice, {Substrate: bob.address});
+ const targetToken = await collection.mintToken(alice, {Substrate: charlie.address});
- // Create a nested token by an administrator
- const nestedToken = await createItemExpectSuccess(bob, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});
- expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: charlie.address});
- expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});
+ // Create an immediately nested token by an administrator
+ const nestedToken = await collection.mintToken(bob, targetToken.nestingAccount());
+ expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
+ expect(await nestedToken.getOwner()).to.be.deep.equal(targetToken.nestingAccount().toLowerCase());
- // Create a token and allow the owner to nest too
- const newToken = await createItemExpectSuccess(alice, collection, 'NFT', charlie.address);
- await transferExpectSuccess(collection, newToken, charlie, {Ethereum: tokenIdToAddress(collection, nestedToken)});
- expect(await getTopmostTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: charlie.address});
- expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, nestedToken).toLowerCase()});
- });
+ // Create a token to be nested and nest
+ const newToken = await collection.mintToken(alice, {Substrate: charlie.address});
+ await newToken.nest(charlie, targetToken);
+ expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
+ expect(await newToken.getOwner()).to.be.deep.equal(targetToken.nestingAccount().toLowerCase());
});
- it('Admin (NFT): allows an Admin to nest a token (Restricted nesting)', async () => {
- await usingApi(async api => {
- const collectionA = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await addCollectionAdminExpectSuccess(alice, collectionA, bob.address);
- const collectionB = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await addCollectionAdminExpectSuccess(alice, collectionB, bob.address);
- await setCollectionPermissionsExpectSuccess(alice, collectionA, {nesting: {collectionAdmin: true, restricted:[collectionA, collectionB]}});
- const targetToken = await createItemExpectSuccess(alice, collectionA, 'NFT', charlie.address);
+ itSub('Admin (NFT): allows an Admin to nest a token (Restricted nesting)', async ({helper}) => {
+ const collectionA = await helper.nft.mintCollection(alice);
+ await collectionA.addAdmin(alice, {Substrate: bob.address});
+ const collectionB = await helper.nft.mintCollection(alice);
+ await collectionB.addAdmin(alice, {Substrate: bob.address});
+ await collectionA.setPermissions(alice, {nesting: {collectionAdmin: true, restricted:[collectionB.collectionId]}});
+ const targetToken = await collectionA.mintToken(alice, {Substrate: charlie.address});
- // Create a nested token
- const nestedToken = await createItemExpectSuccess(bob, collectionB, 'NFT', {Ethereum: tokenIdToAddress(collectionA, targetToken)});
- expect(await getTopmostTokenOwner(api, collectionB, nestedToken)).to.be.deep.equal({Substrate: charlie.address});
- expect(await getTokenOwner(api, collectionB, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collectionA, targetToken).toLowerCase()});
+ // Create an immediately nested token
+ const nestedToken = await collectionB.mintToken(bob, targetToken.nestingAccount());
+ expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
+ expect(await nestedToken.getOwner()).to.be.deep.equal(targetToken.nestingAccount().toLowerCase());
- // Create a token to be nested and nest
- const newToken = await createItemExpectSuccess(bob, collectionB, 'NFT');
- await transferExpectSuccess(collectionB, newToken, bob, {Ethereum: tokenIdToAddress(collectionA, targetToken)});
- expect(await getTopmostTokenOwner(api, collectionB, newToken)).to.be.deep.equal({Substrate: charlie.address});
- expect(await getTokenOwner(api, collectionB, newToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collectionA, targetToken).toLowerCase()});
- });
+ // Create a token to be nested and nest
+ const newToken = await collectionB.mintToken(bob);
+ await newToken.nest(bob, targetToken);
+ expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
+ expect(await newToken.getOwner()).to.be.deep.equal(targetToken.nestingAccount().toLowerCase());
});
// ---------- Non-Fungible ----------
- it('NFT: allows an Owner to nest/unnest their token', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
+ itSub('NFT: allows an Owner to nest/unnest their token', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true}}});
+ await collection.addToAllowList(alice, {Substrate: charlie.address});
+ const targetToken = await collection.mintToken(charlie);
+ await collection.addToAllowList(alice, targetToken.nestingAccount());
- // Create a nested token
- const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});
- expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: alice.address});
- expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});
+ // Create an immediately nested token
+ const nestedToken = await collection.mintToken(charlie, targetToken.nestingAccount());
+ expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
+ expect(await nestedToken.getOwner()).to.be.deep.equal(targetToken.nestingAccount().toLowerCase());
- // Create a token to be nested and nest
- const newToken = await createItemExpectSuccess(alice, collection, 'NFT');
- await transferExpectSuccess(collection, newToken, alice, {Ethereum: tokenIdToAddress(collection, targetToken)});
- expect(await getTopmostTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});
- expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});
- });
+ // Create a token to be nested and nest
+ const newToken = await collection.mintToken(charlie);
+ await newToken.nest(charlie, targetToken);
+ expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
+ expect(await newToken.getOwner()).to.be.deep.equal(targetToken.nestingAccount().toLowerCase());
});
- it('NFT: allows an Owner to nest/unnest their token (Restricted nesting)', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true, restricted:[collection]}});
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
+ itSub('NFT: allows an Owner to nest/unnest their token (Restricted nesting)', async ({helper}) => {
+ const collectionA = await helper.nft.mintCollection(alice);
+ const collectionB = await helper.nft.mintCollection(alice);
+ //await collectionB.addAdmin(alice, {Substrate: bob.address});
+ const targetToken = await collectionA.mintToken(alice, {Substrate: charlie.address});
- // Create a nested token
- const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});
- expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: alice.address});
- expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});
+ await collectionA.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true, restricted:[collectionB.collectionId]}});
+ await collectionA.addToAllowList(alice, {Substrate: charlie.address});
+ await collectionA.addToAllowList(alice, targetToken.nestingAccount());
+
+ await collectionB.setPermissions(alice, {access: 'AllowList', mintMode: true});
+ await collectionB.addToAllowList(alice, {Substrate: charlie.address});
+ await collectionB.addToAllowList(alice, targetToken.nestingAccount());
- // Create a token to be nested and nest
- const newToken = await createItemExpectSuccess(alice, collection, 'NFT');
- await transferExpectSuccess(collection, newToken, alice, {Ethereum: tokenIdToAddress(collection, targetToken)});
- expect(await getTopmostTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});
- expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});
- });
+ // Create an immediately nested token
+ const nestedToken = await collectionB.mintToken(charlie, targetToken.nestingAccount());
+ expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
+ expect(await nestedToken.getOwner()).to.be.deep.equal(targetToken.nestingAccount().toLowerCase());
+
+ // Create a token to be nested and nest
+ const newToken = await collectionB.mintToken(charlie);
+ await newToken.nest(charlie, targetToken);
+ expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
+ expect(await newToken.getOwner()).to.be.deep.equal(targetToken.nestingAccount().toLowerCase());
});
// ---------- Fungible ----------
- it('Fungible: allows an Owner to nest/unnest their token', async () => {
- await usingApi(async api => {
- const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true}});
- const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT', {Substrate: alice.address});
- const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
+ itSub('Fungible: allows an Owner to nest/unnest their token', async ({helper}) => {
+ const collectionNFT = await helper.nft.mintCollection(alice, {permissions: {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true}}});
+ const collectionFT = await helper.ft.mintCollection(alice);
+ const targetToken = await collectionNFT.mintToken(alice, {Substrate: charlie.address});
- const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ await collectionNFT.addToAllowList(alice, {Substrate: charlie.address});
+ await collectionNFT.addToAllowList(alice, targetToken.nestingAccount());
- // Create a nested token
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collectionFT,
- targetAddress,
- {Fungible: {Value: 10}},
- ))).to.not.be.rejected;
+ await collectionFT.setPermissions(alice, {access: 'AllowList', mintMode: true});
+ await collectionFT.addToAllowList(alice, {Substrate: charlie.address});
+ await collectionFT.addToAllowList(alice, targetToken.nestingAccount());
- // Nest a new token
- const newToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');
- await transferExpectSuccess(collectionFT, newToken, alice, targetAddress, 1, 'Fungible');
- });
+ // Create an immediately nested token
+ await collectionFT.mint(charlie, 5n, targetToken.nestingAccount());
+ expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(5n);
+
+ // Create a token to be nested and nest
+ await collectionFT.mint(charlie, 5n);
+ await collectionFT.transfer(charlie, targetToken.nestingAccount(), 2n);
+ expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(7n);
});
- it('Fungible: allows an Owner to nest/unnest their token (Restricted nesting)', async () => {
- await usingApi(async api => {
- const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT', {Substrate: alice.address});
- const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
+ itSub('Fungible: allows an Owner to nest/unnest their token (Restricted nesting)', async ({helper}) => {
+ const collectionNFT = await helper.nft.mintCollection(alice);
+ const collectionFT = await helper.ft.mintCollection(alice);
+ const targetToken = await collectionNFT.mintToken(alice, {Substrate: charlie.address});
- const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ await collectionNFT.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true, restricted:[collectionFT.collectionId]}});
+ await collectionNFT.addToAllowList(alice, {Substrate: charlie.address});
+ await collectionNFT.addToAllowList(alice, targetToken.nestingAccount());
- await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true, restricted: [collectionFT]}});
+ await collectionFT.setPermissions(alice, {access: 'AllowList', mintMode: true});
+ await collectionFT.addToAllowList(alice, {Substrate: charlie.address});
+ await collectionFT.addToAllowList(alice, targetToken.nestingAccount());
- // Create a nested token
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collectionFT,
- targetAddress,
- {Fungible: {Value: 10}},
- ))).to.not.be.rejected;
+ // Create an immediately nested token
+ await collectionFT.mint(charlie, 5n, targetToken.nestingAccount());
+ expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(5n);
- // Nest a new token
- const newToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');
- await transferExpectSuccess(collectionFT, newToken, alice, targetAddress, 1, 'Fungible');
- });
+ // Create a token to be nested and nest
+ await collectionFT.mint(charlie, 5n);
+ await collectionFT.transfer(charlie, targetToken.nestingAccount(), 2n);
+ expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(7n);
});
// ---------- Re-Fungible ----------
- it('ReFungible: allows an Owner to nest/unnest their token', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
+ itSub.ifWithPallets('ReFungible: allows an Owner to nest/unnest their token', [Pallets.ReFungible], async ({helper}) => {
+ const collectionNFT = await helper.nft.mintCollection(alice, {permissions: {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true}}});
+ const collectionRFT = await helper.rft.mintCollection(alice);
+ const targetToken = await collectionNFT.mintToken(alice, {Substrate: charlie.address});
- await usingApi(async api => {
- const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true}});
- const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT', {Substrate: alice.address});
- const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
+ await collectionNFT.addToAllowList(alice, {Substrate: charlie.address});
+ await collectionNFT.addToAllowList(alice, targetToken.nestingAccount());
- const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
+ await collectionRFT.setPermissions(alice, {access: 'AllowList', mintMode: true});
+ await collectionRFT.addToAllowList(alice, {Substrate: charlie.address});
+ await collectionRFT.addToAllowList(alice, targetToken.nestingAccount());
- // Create a nested token
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collectionRFT,
- targetAddress,
- {ReFungible: {pieces: 100}},
- ))).to.not.be.rejected;
+ // Create an immediately nested token
+ const nestedToken = await collectionRFT.mintToken(charlie, 5n, targetToken.nestingAccount());
+ expect(await nestedToken.getBalance(targetToken.nestingAccount())).to.be.equal(5n);
- // Nest a new token
- const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');
- await transferExpectSuccess(collectionRFT, newToken, alice, targetAddress, 100, 'ReFungible');
- });
+ // Create a token to be nested and nest
+ const newToken = await collectionRFT.mintToken(charlie, 5n);
+ await newToken.transfer(charlie, targetToken.nestingAccount(), 2n);
+ expect(await newToken.getBalance(targetToken.nestingAccount())).to.be.equal(2n);
});
- it('ReFungible: allows an Owner to nest/unnest their token (Restricted nesting)', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
+ itSub.ifWithPallets('ReFungible: allows an Owner to nest/unnest their token (Restricted nesting)', [Pallets.ReFungible], async ({helper}) => {
+ const collectionNFT = await helper.nft.mintCollection(alice);
+ const collectionRFT = await helper.rft.mintCollection(alice);
+ const targetToken = await collectionNFT.mintToken(alice, {Substrate: charlie.address});
- await usingApi(async api => {
- const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT', {Substrate: alice.address});
- const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
+ await collectionNFT.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true, restricted:[collectionRFT.collectionId]}});
+ await collectionNFT.addToAllowList(alice, {Substrate: charlie.address});
+ await collectionNFT.addToAllowList(alice, targetToken.nestingAccount());
- const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
+ await collectionRFT.setPermissions(alice, {access: 'AllowList', mintMode: true});
+ await collectionRFT.addToAllowList(alice, {Substrate: charlie.address});
+ await collectionRFT.addToAllowList(alice, targetToken.nestingAccount());
- await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true, restricted:[collectionRFT]}});
+ // Create an immediately nested token
+ const nestedToken = await collectionRFT.mintToken(charlie, 5n, targetToken.nestingAccount());
+ expect(await nestedToken.getBalance(targetToken.nestingAccount())).to.be.equal(5n);
- // Create a nested token
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collectionRFT,
- targetAddress,
- {ReFungible: {pieces: 100}},
- ))).to.not.be.rejected;
-
- // Nest a new token
- const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');
- await transferExpectSuccess(collectionRFT, newToken, alice, targetAddress, 100, 'ReFungible');
- });
+ // Create a token to be nested and nest
+ const newToken = await collectionRFT.mintToken(charlie, 5n);
+ await newToken.transfer(charlie, targetToken.nestingAccount(), 2n);
+ expect(await newToken.getBalance(targetToken.nestingAccount())).to.be.equal(2n);
});
});
-describe('Negative Test: Nesting', async() => {
+describe('Negative Test: Nesting', () => {
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
+
before(async () => {
- await usingApi(async (_, privateKeyWrapper) => {
- alice = privateKeyWrapper('//Alice');
- bob = privateKeyWrapper('//Bob');
+ await usingPlaygrounds(async (helper, privateKey) => {
+ const donor = privateKey('//Alice');
+ [alice, bob] = await helper.arrange.createAccounts([100n, 50n], donor);
});
});
- it('Disallows excessive token nesting', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
-
- const maxNestingLevel = 5;
- let prevToken = targetToken;
+ itSub('Disallows excessive token nesting', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+ let token = await collection.mintToken(alice);
- // Create a nested-token matryoshka
- for (let i = 0; i < maxNestingLevel; i++) {
- const nestedToken = await createItemExpectSuccess(
- alice,
- collection,
- 'NFT',
- {Ethereum: tokenIdToAddress(collection, prevToken)},
- );
+ const maxNestingLevel = 5;
- prevToken = nestedToken;
- }
-
- // The nesting depth is limited by `maxNestingLevel`
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collection,
- {Ethereum: tokenIdToAddress(collection, prevToken)},
- {nft: {}} as any,
- )), 'while creating nested token').to.be.rejectedWith(/^structure\.DepthLimit$/);
+ // Create a nested-token matryoshka
+ for (let i = 0; i < maxNestingLevel; i++) {
+ token = await collection.mintToken(alice, token.nestingAccount());
+ }
- expect(await getTopmostTokenOwner(api, collection, prevToken)).to.be.deep.equal({Substrate: alice.address});
- });
+ // The nesting depth is limited by `maxNestingLevel`
+ await expect(collection.mintToken(alice, token.nestingAccount()))
+ .to.be.rejectedWith(/structure\.DepthLimit/);
+ expect(await token.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address});
+ expect(await token.getChildren()).to.be.length(0);
});
// ---------- Admin ------------
- it('Admin (NFT): disallows an Admin to operate nesting when only TokenOwner is allowed', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
- await addCollectionAdminExpectSuccess(alice, collection, bob.address);
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
+ itSub('Admin (NFT): disallows an Admin to operate nesting when only TokenOwner is allowed', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+ await collection.addAdmin(alice, {Substrate: bob.address});
+ const targetToken = await collection.mintToken(alice);
- // Try to create a nested token as collection admin when it's disallowed
- await expect(executeTransaction(api, bob, api.tx.unique.createItem(
- collection,
- {Ethereum: tokenIdToAddress(collection, targetToken)},
- {nft: {}} as any,
- )), 'while creating nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
+ // Try to create an immediately nested token as collection admin when it's disallowed
+ await expect(collection.mintToken(bob, targetToken.nestingAccount()))
+ .to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
- // Try to create and nest a token in the wrong collection
- const newToken = await createItemExpectSuccess(bob, collection, 'NFT');
- await expect(executeTransaction(
- api,
- bob,
- api.tx.unique.transfer({Ethereum: tokenIdToAddress(collection, targetToken)}, collection, newToken, 1),
- ), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
- expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: bob.address});
- });
+ // Try to create a token to be nested and nest
+ const newToken = await collection.mintToken(bob);
+ await expect(newToken.nest(bob, targetToken))
+ .to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
+
+ expect(await targetToken.getChildren()).to.be.length(0);
+ expect(await newToken.getOwner()).to.be.deep.equal({Substrate: bob.address});
});
- it('Admin (NFT): disallows a Token Owner to operate nesting when only Admin is allowed', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {collectionAdmin: true}});
- await addToAllowListExpectSuccess(alice, collection, bob.address);
- await enableAllowListExpectSuccess(alice, collection);
- await enablePublicMintingExpectSuccess(alice, collection);
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
+ itSub('Admin (NFT): disallows a Token Owner to operate nesting when only Admin is allowed', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {access: 'AllowList', mintMode: true, nesting: {collectionAdmin: true}}});
+ const targetToken = await collection.mintToken(alice, {Substrate: bob.address});
+ await collection.addToAllowList(alice, {Substrate: bob.address});
+ await collection.addToAllowList(alice, targetToken.nestingAccount());
+
+ // Try to create a nested token as token owner when it's disallowed
+ await expect(collection.mintToken(bob, targetToken.nestingAccount()))
+ .to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
- // Try to create a nested token as collection admin when it's disallowed
- await expect(executeTransaction(api, bob, api.tx.unique.createItem(
- collection,
- {Ethereum: tokenIdToAddress(collection, targetToken)},
- {nft: {}} as any,
- )), 'while creating nested token').to.be.rejectedWith(/common\.AddressNotInAllowlist/);
+ // Try to create a token to be nested and nest
+ const newToken = await collection.mintToken(bob);
+ await expect(newToken.nest(bob, targetToken))
+ .to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
- // Try to create and nest a token in the wrong collection
- const newToken = await createItemExpectSuccess(bob, collection, 'NFT');
- await expect(executeTransaction(
- api,
- bob,
- api.tx.unique.transfer({Ethereum: tokenIdToAddress(collection, targetToken)}, collection, newToken, 1),
- ), 'while nesting new token').to.be.rejectedWith(/common\.AddressNotInAllowlist/);
- expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: bob.address});
- });
+ expect(await targetToken.getChildren()).to.be.length(0);
+ expect(await newToken.getOwner()).to.be.deep.equal({Substrate: bob.address});
});
- it('Admin (NFT): disallows an Admin to nest and unnest someone else\'s token', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionLimitsExpectSuccess(alice, collection, {ownerCanTransfer: true});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {collectionAdmin: true}});
+ itSub('Admin (NFT): disallows an Admin to unnest someone else\'s token', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {limits: {ownerCanTransfer: true}, permissions: {access: 'AllowList', mintMode: true, nesting: {collectionAdmin: true}}});
+ //await collection.addAdmin(alice, {Substrate: bob.address});
+ const targetToken = await collection.mintToken(alice, {Substrate: bob.address});
+ await collection.addToAllowList(alice, {Substrate: bob.address});
+ await collection.addToAllowList(alice, targetToken.nestingAccount());
- await addToAllowListExpectSuccess(alice, collection, bob.address);
- await enableAllowListExpectSuccess(alice, collection);
- await enablePublicMintingExpectSuccess(alice, collection);
-
- // Create a token to attempt to be nested into
- const targetToken = await createItemExpectSuccess(bob, collection, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()};
+ // Try to nest somebody else's token
+ const newToken = await collection.mintToken(bob);
+ await expect(newToken.nest(alice, targetToken))
+ .to.be.rejectedWith(/common\.NoPermission/);
- // Try to nest somebody else's token
- const newToken = await createItemExpectSuccess(bob, collection, 'NFT');
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.transferFrom(targetAddress, {Substrate: bob.address}, collection, newToken, 1),
- ), 'while nesting another\'s token token').to.be.rejectedWith(/common\.AddressNotInAllowlist/);
- expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: bob.address});
+ // Try to unnest a token belonging to someone else as collection admin
+ const nestedToken = await collection.mintToken(alice, targetToken.nestingAccount());
+ await expect(nestedToken.unnest(alice, targetToken, {Substrate: bob.address}))
+ .to.be.rejectedWith(/common\.AddressNotInAllowlist/);
- // Nest a token as admin and try to unnest it, now belonging to someone else
- const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', targetAddress);
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.transferFrom(targetAddress, normalizeAccountId(alice), collection, nestedToken, 1),
- ), 'while unnesting another\'s token').to.be.rejectedWith(/common\.AddressNotInAllowlist/);
- expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal(targetAddress);
- expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: bob.address});
- });
+ expect(await targetToken.getChildren()).to.be.length(1);
+ expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address});
+ expect(await nestedToken.getOwner()).to.be.deep.equal(targetToken.nestingAccount().toLowerCase());
});
- it('Admin (NFT): disallows an Admin to nest a token from an unlisted collection (Restricted nesting)', async () => {
- await usingApi(async api => {
- const collectionA = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- const collectionB = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collectionA, {nesting: {collectionAdmin: true, restricted:[collectionA]}});
+ itSub('Admin (NFT): disallows an Admin to nest a token from an unlisted collection (Restricted nesting)', async ({helper}) => {
+ const collectionA = await helper.nft.mintCollection(alice);
+ const collectionB = await helper.nft.mintCollection(alice);
+ await collectionA.setPermissions(alice, {nesting: {collectionAdmin: true, restricted: [collectionA.collectionId]}});
+ const targetToken = await collectionA.mintToken(alice);
- // Create a token to attempt to be nested into
- const targetToken = await createItemExpectSuccess(alice, collectionA, 'NFT');
+ // Try to create a nested token from another collection
+ await expect(collectionB.mintToken(alice, targetToken.nestingAccount()))
+ .to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
- // Try to create and nest a token in the wrong collection
- const newToken = await createItemExpectSuccess(alice, collectionB, 'NFT');
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.transfer({Ethereum: tokenIdToAddress(collectionA, targetToken)}, collectionB, newToken, 1),
- ), 'while nesting a foreign token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
- expect(await getTokenOwner(api, collectionB, newToken)).to.be.deep.equal({Substrate: alice.address});
- });
+ // Create a token in another collection yet to be nested and try to nest
+ const newToken = await collectionB.mintToken(alice);
+ await expect(newToken.nest(alice, targetToken))
+ .to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
+
+ expect(await targetToken.getChildren()).to.be.length(0);
+ expect(await newToken.getOwner()).to.be.deep.equal({Substrate: alice.address});
});
// ---------- Non-Fungible ----------
- it('NFT: disallows to nest token if nesting is disabled', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {}});
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
+ itSub('NFT: disallows to nest token if nesting is disabled', async ({helper}) => {
+ // Collection is implicitly not allowed nesting at creation
+ const collection = await helper.nft.mintCollection(alice);
+ const targetToken = await collection.mintToken(alice);
- // Try to create a nested token
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collection,
- {Ethereum: tokenIdToAddress(collection, targetToken)},
- {nft: {}} as any,
- )), 'while creating nested token').to.be.rejectedWith(/^common\.UserIsNotAllowedToNest$/);
+ // Try to create a nested token as token owner when it's disallowed
+ await expect(collection.mintToken(alice, targetToken.nestingAccount()))
+ .to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
- // Create a token to be nested
- const newToken = await createItemExpectSuccess(alice, collection, 'NFT');
- // Try to nest
- await expect(executeTransaction(api, alice, api.tx.unique.transfer({Ethereum: tokenIdToAddress(collection, targetToken)}, collection, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
- expect(await getTopmostTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});
- expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});
- });
+ // Try to create a token to be nested and nest
+ const newToken = await collection.mintToken(alice);
+ await expect(newToken.nest(alice, targetToken))
+ .to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
+
+ expect(await targetToken.getChildren()).to.be.length(0);
+ expect(await newToken.getOwner()).to.be.deep.equal({Substrate: alice.address});
});
- it('NFT: disallows a non-Owner to nest someone else\'s token', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
+ itSub('NFT: disallows a non-Owner to nest someone else\'s token', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice);
+ const targetToken = await collection.mintToken(alice);
- await addToAllowListExpectSuccess(alice, collection, bob.address);
- await enableAllowListExpectSuccess(alice, collection);
- await enablePublicMintingExpectSuccess(alice, collection);
-
- // Create a token to attempt to be nested into
- const targetToken = await createItemExpectSuccess(bob, collection, 'NFT');
+ await collection.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true}});
+ await collection.addToAllowList(alice, {Substrate: bob.address});
+ await collection.addToAllowList(alice, targetToken.nestingAccount());
- // Try to create a nested token in the wrong collection
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collection,
- {Ethereum: tokenIdToAddress(collection, targetToken)},
- {nft: {}} as any,
- )), 'while creating nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
+ // Try to create a token to be nested and nest
+ const newToken = await collection.mintToken(alice);
+ await expect(newToken.nest(bob, targetToken)).to.be.rejectedWith(/common\.NoPermission/);
- // Try to create and nest a token in the wrong collection
- const newToken = await createItemExpectSuccess(alice, collection, 'NFT');
- await expect(executeTransaction(api, alice, api.tx.unique.transfer({Ethereum: tokenIdToAddress(collection, targetToken)}, collection, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.AddressNotInAllowlist/);
- expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});
- });
+ expect(await targetToken.getChildren()).to.be.length(0);
+ expect(await newToken.getOwner()).to.be.deep.equal({Substrate: alice.address});
});
- it('NFT: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true, restricted:[collection]}});
+ itSub('NFT: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice);
+ const targetToken = await collection.mintToken(alice);
- await addToAllowListExpectSuccess(alice, collection, bob.address);
- await enableAllowListExpectSuccess(alice, collection);
- await enablePublicMintingExpectSuccess(alice, collection);
+ await collection.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true}});
+ await collection.addToAllowList(alice, {Substrate: bob.address});
+ await collection.addToAllowList(alice, targetToken.nestingAccount());
- // Create a token to attempt to be nested into
- const targetToken = await createItemExpectSuccess(bob, collection, 'NFT');
+ const collectionB = await helper.nft.mintCollection(alice, {permissions: {access: 'AllowList', mintMode: true}});
+ await collectionB.addToAllowList(alice, {Substrate: bob.address});
+ await collectionB.addToAllowList(alice, targetToken.nestingAccount());
- // Try to create a nested token in the wrong collection
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collection,
- {Ethereum: tokenIdToAddress(collection, targetToken)},
- {nft: {}} as any,
- )), 'while creating nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
+ // Try to create a token to be nested and nest
+ const newToken = await collectionB.mintToken(alice);
+ await expect(newToken.nest(bob, targetToken)).to.be.rejectedWith(/common\.NoPermission/);
- // Try to create and nest a token in the wrong collection
- const newToken = await createItemExpectSuccess(alice, collection, 'NFT');
- await expect(executeTransaction(api, alice, api.tx.unique.transfer({Ethereum: tokenIdToAddress(collection, targetToken)}, collection, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.AddressNotInAllowlist/);
- expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});
- });
+ expect(await targetToken.getChildren()).to.be.length(0);
+ expect(await newToken.getOwner()).to.be.deep.equal({Substrate: alice.address});
});
- it('NFT: disallows to nest token in an unlisted collection', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true, restricted:[]}});
+ itSub('NFT: disallows to nest token in an unlisted collection', async ({helper}) => {
+ // Create collection with restricted nesting -- even self is not allowed
+ const collection = await helper.nft.mintCollection(alice, {permissions: {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true, restricted: []}}});
+ const targetToken = await collection.mintToken(alice, {Substrate: bob.address});
- // Create a token to attempt to be nested into
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
+ await collection.addToAllowList(alice, {Substrate: bob.address});
+ await collection.addToAllowList(alice, targetToken.nestingAccount());
- // Try to create a nested token in the wrong collection
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collection,
- {Ethereum: tokenIdToAddress(collection, targetToken)},
- {nft: {}} as any,
- )), 'while creating nested token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
-
- // Try to create and nest a token in the wrong collection
- const newToken = await createItemExpectSuccess(alice, collection, 'NFT');
- await expect(executeTransaction(api, alice, api.tx.unique.transfer({Ethereum: tokenIdToAddress(collection, targetToken)}, collection, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
- expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});
- });
+ // Try to mint in own collection after allowlisting the accounts
+ await expect(collection.mintToken(bob, targetToken.nestingAccount()))
+ .to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
});
// ---------- Fungible ----------
-
- it('Fungible: disallows to nest token if nesting is disabled', async () => {
- await usingApi(async api => {
- const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {}});
- const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
-
- const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
- // Try to create a nested token
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collectionFT,
- targetAddress,
- {Fungible: {Value: 10}},
- )), 'while creating nested token').to.be.rejectedWith(/^common\.UserIsNotAllowedToNest$/);
+ itSub('Fungible: disallows to nest token if nesting is disabled', async ({helper}) => {
+ const collectionNFT = await helper.nft.mintCollection(alice);
+ const collectionFT = await helper.ft.mintCollection(alice);
+ const targetToken = await collectionNFT.mintToken(alice);
- // Create a token to be nested
- const newToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');
- // Try to nest
- await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
+ // Try to create an immediately nested token
+ await expect(collectionFT.mint(alice, 5n, targetToken.nestingAccount()))
+ .to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
- // Create another token to be nested
- const newToken2 = await createItemExpectSuccess(alice, collectionFT, 'Fungible');
- // Try to nest inside a fungible token
- await expect(executeTransaction(api, alice, api.tx.unique.transfer({Ethereum: tokenIdToAddress(collectionFT, newToken)}, collectionFT, newToken2, 1)), 'while nesting new token inside fungible').to.be.rejectedWith(/fungible\.FungibleDisallowsNesting/);
- });
+ // Try to create a token to be nested and nest
+ await collectionFT.mint(alice, 5n);
+ await expect(collectionFT.transfer(alice, targetToken.nestingAccount(), 2n))
+ .to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
+ expect(await collectionFT.getBalance({Substrate: alice.address})).to.be.equal(5n);
});
- it('Fungible: disallows a non-Owner to nest someone else\'s token', async () => {
- await usingApi(async api => {
- const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true}});
+ itSub('Fungible: disallows a non-Owner to unnest someone else\'s token', async ({helper}) => {
+ const collectionNFT = await helper.nft.mintCollection(alice, {permissions: {nesting: {collectionAdmin: true, tokenOwner: true}}});
+ const collectionFT = await helper.ft.mintCollection(alice);
+ const targetToken = await collectionNFT.mintToken(alice, {Substrate: bob.address});
- await addToAllowListExpectSuccess(alice, collectionNFT, bob.address);
- await enableAllowListExpectSuccess(alice, collectionNFT);
- await enablePublicMintingExpectSuccess(alice, collectionNFT);
+ // Nest some tokens as Alice into Bob's token
+ await collectionFT.mint(alice, 5n, targetToken.nestingAccount());
- // Create a token to attempt to be nested into
- const targetToken = await createItemExpectSuccess(bob, collectionNFT, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
-
- const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
-
- // Try to create a nested token in the wrong collection
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collectionFT,
- targetAddress,
- {Fungible: {Value: 10}},
- )), 'while creating nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
-
- // Try to create and nest a token in the wrong collection
- const newToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');
- await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
- });
+ // Try to pull it out
+ await expect(collectionFT.transferFrom(alice, targetToken.nestingAccount(), {Substrate: bob.address}, 1n))
+ .to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+ expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(5n);
});
- it('Fungible: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', async () => {
- await usingApi(async api => {
- const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await addToAllowListExpectSuccess(alice, collectionNFT, bob.address);
- await enableAllowListExpectSuccess(alice, collectionNFT);
- await enablePublicMintingExpectSuccess(alice, collectionNFT);
-
- // Create a token to attempt to be nested into
- const targetToken = await createItemExpectSuccess(bob, collectionNFT, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
+ itSub('Fungible: disallows a non-Owner to unnest someone else\'s token (Restricted nesting)', async ({helper}) => {
+ const collectionNFT = await helper.nft.mintCollection(alice);
+ const collectionFT = await helper.ft.mintCollection(alice);
+ const targetToken = await collectionNFT.mintToken(alice, {Substrate: bob.address});
- const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
- await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true, restricted:[collectionFT]}});
+ await collectionNFT.setPermissions(alice, {nesting: {collectionAdmin: true, tokenOwner: true, restricted: [collectionFT.collectionId]}});
- // Try to create a nested token in the wrong collection
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collectionFT,
- targetAddress,
- {Fungible: {Value: 10}},
- )), 'while creating nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
+ // Nest some tokens as Alice into Bob's token
+ await collectionFT.mint(alice, 5n, targetToken.nestingAccount());
- // Try to create and nest a token in the wrong collection
- const newToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');
- await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
- });
+ // Try to pull it out as Alice still
+ await expect(collectionFT.transferFrom(alice, targetToken.nestingAccount(), {Substrate: bob.address}, 1n))
+ .to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+ expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(5n);
});
- it('Fungible: disallows to nest token in an unlisted collection', async () => {
- await usingApi(async api => {
- const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true, restricted:[]}});
-
- // Create a token to attempt to be nested into
- const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
+ itSub('Fungible: disallows to nest token in an unlisted collection', async ({helper}) => {
+ const collectionNFT = await helper.nft.mintCollection(alice, {permissions: {nesting: {collectionAdmin: true, tokenOwner: true, restricted: []}}});
+ const collectionFT = await helper.ft.mintCollection(alice);
+ const targetToken = await collectionNFT.mintToken(alice);
- const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ // Try to mint an immediately nested token
+ await expect(collectionFT.mint(alice, 5n, targetToken.nestingAccount()))
+ .to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
- // Try to create a nested token in the wrong collection
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collectionFT,
- targetAddress,
- {Fungible: {Value: 10}},
- )), 'while creating a nested token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
+ // Mint a token and try to nest it
+ await collectionFT.mint(alice, 5n);
+ await expect(collectionFT.transfer(alice, targetToken.nestingAccount(), 1n))
+ .to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
- // Try to create and nest a token in the wrong collection
- const newToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');
- await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
- });
+ expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(0n);
+ expect(await collectionFT.getBalance({Substrate: alice.address})).to.be.equal(5n);
});
// ---------- Re-Fungible ----------
-
- it('ReFungible: disallows to nest token if nesting is disabled', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- await usingApi(async api => {
- const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {}});
- const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
-
- const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
-
- // Create a nested token
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collectionRFT,
- targetAddress,
- {ReFungible: {pieces: 100}},
- )), 'while creating a nested token').to.be.rejectedWith(/^common\.UserIsNotAllowedToNest$/);
+ itSub.ifWithPallets('ReFungible: disallows to nest token if nesting is disabled', [Pallets.ReFungible], async ({helper}) => {
+ const collectionNFT = await helper.nft.mintCollection(alice);
+ const collectionRFT = await helper.rft.mintCollection(alice);
+ const targetToken = await collectionNFT.mintToken(alice);
- // Create a token to be nested
- const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');
- // Try to nest
- await transferExpectFailure(collectionRFT, newToken, alice, targetAddress, 100);
- // Try to nest
- await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionRFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
+ // Try to create an immediately nested token
+ await expect(collectionRFT.mintToken(alice, 5n, targetToken.nestingAccount()))
+ .to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
- // Create another token to be nested
- const newToken2 = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');
- // Try to nest inside a fungible token
- await expect(executeTransaction(api, alice, api.tx.unique.transfer({Ethereum: tokenIdToAddress(collectionRFT, newToken)}, collectionRFT, newToken2, 1)), 'while nesting new token inside refungible').to.be.rejectedWith(/refungible\.RefungibleDisallowsNesting/);
- });
+ // Try to create a token to be nested and nest
+ const token = await collectionRFT.mintToken(alice, 5n);
+ await expect(token.transfer(alice, targetToken.nestingAccount(), 2n))
+ .to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
+ expect(await token.getBalance({Substrate: alice.address})).to.be.equal(5n);
});
-
- it('ReFungible: disallows a non-Owner to nest someone else\'s token', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- await usingApi(async api => {
- const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true}});
+ itSub.ifWithPallets('ReFungible: disallows a non-Owner to nest someone else\'s token', [Pallets.ReFungible], async ({helper}) => {
+ const collectionNFT = await helper.nft.mintCollection(alice);
+ const collectionRFT = await helper.rft.mintCollection(alice);
+ const targetToken = await collectionNFT.mintToken(alice);
- await addToAllowListExpectSuccess(alice, collectionNFT, bob.address);
- await enableAllowListExpectSuccess(alice, collectionNFT);
- await enablePublicMintingExpectSuccess(alice, collectionNFT);
+ await collectionNFT.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true}});
+ await collectionNFT.addToAllowList(alice, {Substrate: bob.address});
+ await collectionNFT.addToAllowList(alice, targetToken.nestingAccount());
- // Create a token to attempt to be nested into
- const targetToken = await createItemExpectSuccess(bob, collectionNFT, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
+ // Try to create a token to be nested and nest
+ const newToken = await collectionRFT.mintToken(alice);
+ await expect(newToken.transfer(bob, targetToken.nestingAccount())).to.be.rejectedWith(/common\.TokenValueTooLow/);
- const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
+ expect(await targetToken.getChildren()).to.be.length(0);
+ expect(await newToken.getBalance({Substrate: alice.address})).to.be.equal(1n);
- // Try to create a nested token in the wrong collection
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collectionRFT,
- targetAddress,
- {ReFungible: {pieces: 100}},
- )), 'while creating a nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
+ // Nest some tokens as Alice into Bob's token
+ await newToken.transfer(alice, targetToken.nestingAccount());
- // Try to create and nest a token in the wrong collection
- const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');
- await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionRFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
- });
+ // Try to pull it out
+ await expect(newToken.transferFrom(bob, targetToken.nestingAccount(), {Substrate: alice.address}, 1n))
+ .to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+ expect(await newToken.getBalance(targetToken.nestingAccount())).to.be.equal(1n);
});
- it('ReFungible: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
+ itSub.ifWithPallets('ReFungible: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', [Pallets.ReFungible], async ({helper}) => {
+ const collectionNFT = await helper.nft.mintCollection(alice);
+ const collectionRFT = await helper.rft.mintCollection(alice);
+ const targetToken = await collectionNFT.mintToken(alice);
- await usingApi(async api => {
- const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await addToAllowListExpectSuccess(alice, collectionNFT, bob.address);
- await enableAllowListExpectSuccess(alice, collectionNFT);
- await enablePublicMintingExpectSuccess(alice, collectionNFT);
+ await collectionNFT.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true, restricted: [collectionRFT.collectionId]}});
+ await collectionNFT.addToAllowList(alice, {Substrate: bob.address});
+ await collectionNFT.addToAllowList(alice, targetToken.nestingAccount());
- // Create a token to attempt to be nested into
- const targetToken = await createItemExpectSuccess(bob, collectionNFT, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
+ // Try to create a token to be nested and nest
+ const newToken = await collectionRFT.mintToken(alice);
+ await expect(newToken.transfer(bob, targetToken.nestingAccount())).to.be.rejectedWith(/common\.TokenValueTooLow/);
- const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
- await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true, restricted:[collectionRFT]}});
+ expect(await targetToken.getChildren()).to.be.length(0);
+ expect(await newToken.getBalance({Substrate: alice.address})).to.be.equal(1n);
- // Try to create a nested token in the wrong collection
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collectionRFT,
- targetAddress,
- {ReFungible: {pieces: 100}},
- )), 'while creating a nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
+ // Nest some tokens as Alice into Bob's token
+ await newToken.transfer(alice, targetToken.nestingAccount());
- // Try to create and nest a token in the wrong collection
- const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');
- await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionRFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
- });
+ // Try to pull it out
+ await expect(newToken.transferFrom(bob, targetToken.nestingAccount(), {Substrate: alice.address}, 1n))
+ .to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+ expect(await newToken.getBalance(targetToken.nestingAccount())).to.be.equal(1n);
});
- it('ReFungible: disallows to nest token to an unlisted collection', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
+ itSub.ifWithPallets('ReFungible: disallows to nest token to an unlisted collection', [Pallets.ReFungible], async ({helper}) => {
+ const collectionNFT = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true, restricted: []}}});
+ const collectionRFT = await helper.rft.mintCollection(alice);
+ const targetToken = await collectionNFT.mintToken(alice);
- await usingApi(async api => {
- const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true, restricted:[]}});
+ // Try to create an immediately nested token
+ await expect(collectionRFT.mintToken(alice, 5n, targetToken.nestingAccount()))
+ .to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
- // Create a token to attempt to be nested into
- const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
-
- const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
-
- // Try to create a nested token in the wrong collection
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collectionRFT,
- targetAddress,
- {ReFungible: {pieces: 100}},
- )), 'while creating a nested token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
-
- // Try to create and nest a token in the wrong collection
- const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');
- await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionRFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
- });
+ // Try to create a token to be nested and nest
+ const token = await collectionRFT.mintToken(alice, 5n);
+ await expect(token.transfer(alice, targetToken.nestingAccount(), 2n))
+ .to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
+ expect(await token.getBalance({Substrate: alice.address})).to.be.equal(5n);
});
});
tests/src/nesting/properties.test.tsdiffbeforeafterboth1import {expect} from 'chai';2import usingApi, {executeTransaction} from '../substrate/substrate-api';3import {4 addCollectionAdminExpectSuccess,5 CollectionMode,6 createCollectionExpectSuccess,7 setCollectionPermissionsExpectSuccess,8 createItemExpectSuccess,9 getCreateCollectionResult,10 transferExpectSuccess,11 requirePallets,12 Pallets,13} from '../util/helpers';14import {IKeyringPair} from '@polkadot/types/types';15import {tokenIdToAddress} from '../eth/util/helpers';1617let alice: IKeyringPair;18let bob: IKeyringPair;19let charlie: IKeyringPair;2021describe('Composite Properties Test', () => {22 before(async () => {23 await usingApi(async (api, privateKeyWrapper) => {24 alice = privateKeyWrapper('//Alice');25 bob = privateKeyWrapper('//Bob');26 });27 });2829 async function testMakeSureSuppliesRequired(mode: CollectionMode) {30 await usingApi(async api => {31 const collectionId = await createCollectionExpectSuccess({mode: mode});3233 const collectionOption = await api.rpc.unique.collectionById(collectionId);34 expect(collectionOption.isSome).to.be.true;35 let collection = collectionOption.unwrap();36 expect(collection.tokenPropertyPermissions.toHuman()).to.be.empty;37 expect(collection.properties.toHuman()).to.be.empty;3839 const propertyPermissions = [40 {key: 'mindgame', permission: {collectionAdmin: true, mutable: false, tokenOwner: true}},41 {key: 'skullduggery', permission: {collectionAdmin: false, mutable: true, tokenOwner: false}},42 ];43 await expect(executeTransaction(44 api, 45 alice, 46 api.tx.unique.setTokenPropertyPermissions(collectionId, propertyPermissions), 47 )).to.not.be.rejected;4849 const collectionProperties = [50 {key: 'black_hole', value: 'LIGO'},51 {key: 'electron', value: 'come bond'}, 52 ];53 await expect(executeTransaction(54 api, 55 alice, 56 api.tx.unique.setCollectionProperties(collectionId, collectionProperties), 57 )).to.not.be.rejected;5859 collection = (await api.rpc.unique.collectionById(collectionId)).unwrap();60 expect(collection.tokenPropertyPermissions.toHuman()).to.be.deep.equal(propertyPermissions);61 expect(collection.properties.toHuman()).to.be.deep.equal(collectionProperties);62 });63 }6465 it('Makes sure collectionById supplies required fields for NFT', async () => {66 await testMakeSureSuppliesRequired({type: 'NFT'});67 });6869 it('Makes sure collectionById supplies required fields for ReFungible', async function() {70 await requirePallets(this, [Pallets.ReFungible]);7172 await testMakeSureSuppliesRequired({type: 'ReFungible'});73 });74});7576// ---------- COLLECTION PROPERTIES7778describe('Integration Test: Collection Properties', () => {79 before(async () => {80 await usingApi(async (api, privateKeyWrapper) => {81 alice = privateKeyWrapper('//Alice');82 bob = privateKeyWrapper('//Bob');83 });84 });8586 it('Reads properties from a collection', async () => {87 await usingApi(async api => {88 const collection = await createCollectionExpectSuccess();89 const properties = (await api.query.common.collectionProperties(collection)).toJSON();90 expect(properties.map).to.be.empty;91 expect(properties.consumedSpace).to.equal(0);92 });93 });949596 async function testSetsPropertiesForCollection(mode: string) {97 await usingApi(async api => {98 const events = await executeTransaction(api, bob, api.tx.unique.createCollectionEx({mode: mode}));99 const {collectionId} = getCreateCollectionResult(events);100101 // As owner102 await expect(executeTransaction(103 api, 104 bob, 105 api.tx.unique.setCollectionProperties(collectionId, [{key: 'electron', value: 'come bond'}]), 106 )).to.not.be.rejected;107108 await addCollectionAdminExpectSuccess(bob, collectionId, alice.address);109110 // As administrator111 await expect(executeTransaction(112 api, 113 alice, 114 api.tx.unique.setCollectionProperties(collectionId, [{key: 'black_hole'}]), 115 )).to.not.be.rejected;116117 const properties = (await api.rpc.unique.collectionProperties(collectionId, ['electron', 'black_hole'])).toHuman();118 expect(properties).to.be.deep.equal([119 {key: 'electron', value: 'come bond'},120 {key: 'black_hole', value: ''},121 ]);122 });123 }124 it('Sets properties for a NFT collection', async () => {125 await testSetsPropertiesForCollection('NFT');126 });127 it('Sets properties for a ReFungible collection', async function() {128 await requirePallets(this, [Pallets.ReFungible]);129130 await testSetsPropertiesForCollection('ReFungible');131 });132133 async function testCheckValidNames(mode: string) {134 await usingApi(async api => {135 const events = await executeTransaction(api, bob, api.tx.unique.createCollectionEx({mode: mode}));136 const {collectionId} = getCreateCollectionResult(events);137 138 // alpha symbols139 await expect(executeTransaction(140 api, 141 bob, 142 api.tx.unique.setCollectionProperties(collectionId, [{key: 'alpha'}]), 143 )).to.not.be.rejected;144 145 // numeric symbols146 await expect(executeTransaction(147 api, 148 bob, 149 api.tx.unique.setCollectionProperties(collectionId, [{key: '123'}]), 150 )).to.not.be.rejected;151 152 // underscore symbol153 await expect(executeTransaction(154 api, 155 bob, 156 api.tx.unique.setCollectionProperties(collectionId, [{key: 'black_hole'}]), 157 )).to.not.be.rejected;158 159 // dash symbol160 await expect(executeTransaction(161 api, 162 bob, 163 api.tx.unique.setCollectionProperties(collectionId, [{key: 'semi-automatic'}]), 164 )).to.not.be.rejected;165 166 // underscore symbol167 await expect(executeTransaction(168 api, 169 bob, 170 api.tx.unique.setCollectionProperties(collectionId, [{key: 'build.rs'}]), 171 )).to.not.be.rejected;172 173 const propertyKeys = ['alpha', '123', 'black_hole', 'semi-automatic', 'build.rs'];174 const properties = (await api.rpc.unique.collectionProperties(collectionId, propertyKeys)).toHuman();175 expect(properties).to.be.deep.equal([176 {key: 'alpha', value: ''},177 {key: '123', value: ''},178 {key: 'black_hole', value: ''},179 {key: 'semi-automatic', value: ''},180 {key: 'build.rs', value: ''},181 ]);182 });183 }184 it('Check valid names for NFT collection properties keys', async () => {185 await testCheckValidNames('NFT');186 });187 it('Check valid names for ReFungible collection properties keys', async function() {188 await requirePallets(this, [Pallets.ReFungible]);189190 await testCheckValidNames('ReFungible');191 });192193 async function testChangesProperties(mode: CollectionMode) {194 await usingApi(async api => {195 const collection = await createCollectionExpectSuccess({mode: mode});196 197 await expect(executeTransaction(198 api, 199 alice, 200 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole'}]), 201 )).to.not.be.rejected;202 203 // Mutate the properties204 await expect(executeTransaction(205 api, 206 alice, 207 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'bonded'}, {key: 'black_hole', value: 'LIGO'}]), 208 )).to.not.be.rejected;209 210 const properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black_hole'])).toHuman();211 expect(properties).to.be.deep.equal([212 {key: 'electron', value: 'bonded'},213 {key: 'black_hole', value: 'LIGO'},214 ]);215 });216 }217 it('Changes properties of a NFT collection', async () => {218 await testChangesProperties({type: 'NFT'});219 });220 it('Changes properties of a ReFungible collection', async function() {221 await requirePallets(this, [Pallets.ReFungible]);222223 await testChangesProperties({type: 'ReFungible'});224 });225226 async function testDeleteProperties(mode: CollectionMode) {227 await usingApi(async api => {228 const collection = await createCollectionExpectSuccess({mode: mode});229 230 await expect(executeTransaction(231 api, 232 alice, 233 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]), 234 )).to.not.be.rejected;235 236 await expect(executeTransaction(237 api, 238 alice, 239 api.tx.unique.deleteCollectionProperties(collection, ['electron']), 240 )).to.not.be.rejected;241 242 const properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black_hole'])).toHuman();243 expect(properties).to.be.deep.equal([244 {key: 'black_hole', value: 'LIGO'},245 ]);246 }); 247 }248 it('Deletes properties of a NFT collection', async () => {249 await testDeleteProperties({type: 'NFT'});250 });251 it('Deletes properties of a ReFungible collection', async function() {252 await requirePallets(this, [Pallets.ReFungible]);253254 await testDeleteProperties({type: 'ReFungible'});255 });256});257258describe('Negative Integration Test: Collection Properties', () => {259 before(async () => {260 await usingApi(async (api, privateKeyWrapper) => {261 alice = privateKeyWrapper('//Alice');262 bob = privateKeyWrapper('//Bob');263 });264 });265 266 async function testFailsSetPropertiesIfNotOwnerOrAdmin(mode: CollectionMode) {267 await usingApi(async api => {268 const collection = await createCollectionExpectSuccess({mode: mode});269 270 await expect(executeTransaction(271 api, 272 bob, 273 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]), 274 )).to.be.rejectedWith(/common\.NoPermission/);275 276 const properties = (await api.query.common.collectionProperties(collection)).toJSON();277 expect(properties.map).to.be.empty;278 expect(properties.consumedSpace).to.equal(0);279 }); 280 }281 it('Fails to set properties in a NFT collection if not its onwer/administrator', async () => {282 await testFailsSetPropertiesIfNotOwnerOrAdmin({type: 'NFT'});283 });284 it('Fails to set properties in a ReFungible collection if not its onwer/administrator', async function() {285 await requirePallets(this, [Pallets.ReFungible]);286287 await testFailsSetPropertiesIfNotOwnerOrAdmin({type: 'ReFungible'});288 });289 290 async function testFailsSetPropertiesThatExeedLimits(mode: CollectionMode) {291 await usingApi(async api => {292 const collection = await createCollectionExpectSuccess({mode: mode});293 const spaceLimit = (await api.query.common.collectionProperties(collection)).toJSON().spaceLimit as number; 294 295 // Mute the general tx parsing error, too many bytes to process296 {297 console.error = () => {};298 await expect(executeTransaction(299 api, 300 alice, 301 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 9))}]), 302 )).to.be.rejected;303 }304 305 let properties = (await api.rpc.unique.collectionProperties(collection, ['electron'])).toJSON();306 expect(properties).to.be.empty;307 308 await expect(executeTransaction(309 api, 310 alice, 311 api.tx.unique.setCollectionProperties(collection, [312 {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 18))}, 313 {key: 'black_hole', value: '0'.repeat(Math.ceil(spaceLimit! / 2))}, 314 ]), 315 )).to.be.rejectedWith(/common\.NoSpaceForProperty/);316 317 properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black hole'])).toJSON();318 expect(properties).to.be.empty;319 }); 320 }321 it('Fails to set properties that exceed the limits (NFT)', async () => {322 await testFailsSetPropertiesThatExeedLimits({type: 'NFT'});323 });324 it('Fails to set properties that exceed the limits (ReFungible)', async function() {325 await requirePallets(this, [Pallets.ReFungible]);326327 await testFailsSetPropertiesThatExeedLimits({type: 'ReFungible'});328 });329 330 async function testFailsSetMorePropertiesThanAllowed(mode: CollectionMode) {331 await usingApi(async api => {332 const collection = await createCollectionExpectSuccess({mode: mode});333 334 const propertiesToBeSet = [];335 for (let i = 0; i < 65; i++) {336 propertiesToBeSet.push({337 key: 'electron_' + i,338 value: Math.random() > 0.5 ? 'high' : 'low',339 });340 }341 342 await expect(executeTransaction(343 api, 344 alice, 345 api.tx.unique.setCollectionProperties(collection, propertiesToBeSet), 346 )).to.be.rejectedWith(/common\.PropertyLimitReached/);347 348 const properties = (await api.query.common.collectionProperties(collection)).toJSON();349 expect(properties.map).to.be.empty;350 expect(properties.consumedSpace).to.equal(0);351 }); 352 }353 it('Fails to set more properties than it is allowed (NFT)', async () => {354 await testFailsSetMorePropertiesThanAllowed({type: 'NFT'});355 });356 it('Fails to set more properties than it is allowed (ReFungible)', async function() {357 await requirePallets(this, [Pallets.ReFungible]);358359 await testFailsSetMorePropertiesThanAllowed({type: 'ReFungible'});360 });361 362 async function testFailsSetPropertiesWithInvalidNames(mode: CollectionMode) {363 await usingApi(async api => {364 const collection = await createCollectionExpectSuccess({mode: mode});365 366 const invalidProperties = [367 [{key: 'electron', value: 'negative'}, {key: 'string theory', value: 'understandable'}],368 [{key: 'Mr/Sandman', value: 'Bring me a gene'}],369 [{key: 'déjà vu', value: 'hmm...'}],370 ];371 372 for (let i = 0; i < invalidProperties.length; i++) {373 await expect(executeTransaction(374 api, 375 alice, 376 api.tx.unique.setCollectionProperties(collection, invalidProperties[i]), 377 ), `on rejecting the new badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);378 }379 380 await expect(executeTransaction(381 api, 382 alice, 383 api.tx.unique.setCollectionProperties(collection, [{key: '', value: 'nothing must not exist'}]), 384 ), 'on rejecting an unnamed property').to.be.rejectedWith(/common\.EmptyPropertyKey/);385 386 await expect(executeTransaction(387 api, 388 alice, 389 api.tx.unique.setCollectionProperties(collection, [390 {key: 'CRISPR-Cas9', value: 'rewriting nature!'},391 ]), 392 ), 'on setting the correctly-but-still-badly-named property').to.not.be.rejected;393 394 const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat('CRISPR-Cas9').concat('');395 396 const properties = (await api.rpc.unique.collectionProperties(collection, keys)).toHuman();397 expect(properties).to.be.deep.equal([398 {key: 'CRISPR-Cas9', value: 'rewriting nature!'},399 ]);400 401 for (let i = 0; i < invalidProperties.length; i++) {402 await expect(executeTransaction(403 api, 404 alice, 405 api.tx.unique.deleteCollectionProperties(collection, invalidProperties[i].map(propertySet => propertySet.key)), 406 ), `on trying to delete the non-existent badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);407 }408 });409 }410 it('Fails to set properties with invalid names (NFT)', async () => {411 await testFailsSetPropertiesWithInvalidNames({type: 'NFT'});412 });413 it('Fails to set properties with invalid names (ReFungible)', async function() {414 await requirePallets(this, [Pallets.ReFungible]);415416 await testFailsSetPropertiesWithInvalidNames({type: 'ReFungible'});417 });418});419420// ---------- ACCESS RIGHTS421422describe('Integration Test: Access Rights to Token Properties', () => {423 before(async () => {424 await usingApi(async (api, privateKeyWrapper) => {425 alice = privateKeyWrapper('//Alice');426 bob = privateKeyWrapper('//Bob');427 });428 });429 430 it('Reads access rights to properties of a collection', async () => {431 await usingApi(async api => {432 const collection = await createCollectionExpectSuccess();433 const propertyRights = (await api.query.common.collectionPropertyPermissions(collection)).toJSON();434 expect(propertyRights).to.be.empty;435 });436 });437 438 async function testSetsAccessRightsToProperties(mode: CollectionMode) {439 await usingApi(async api => {440 const collection = await createCollectionExpectSuccess({mode: mode});441 442 await expect(executeTransaction(443 api, 444 alice, 445 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true}}]), 446 )).to.not.be.rejected;447 448 await addCollectionAdminExpectSuccess(alice, collection, bob.address);449 450 await expect(executeTransaction(451 api, 452 alice, 453 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'mindgame', permission: {collectionAdmin: true, tokenOwner: false}}]), 454 )).to.not.be.rejected;455 456 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery', 'mindgame'])).toHuman();457 expect(propertyRights).to.be.deep.equal([458 {key: 'skullduggery', permission: {'mutable': true, 'collectionAdmin': false, 'tokenOwner': false}},459 {key: 'mindgame', permission: {'mutable': false, 'collectionAdmin': true, 'tokenOwner': false}},460 ]);461 }); 462 }463 it('Sets access rights to properties of a collection (NFT)', async () => {464 await testSetsAccessRightsToProperties({type: 'NFT'});465 });466 it('Sets access rights to properties of a collection (ReFungible)', async function() {467 await requirePallets(this, [Pallets.ReFungible]);468469 await testSetsAccessRightsToProperties({type: 'ReFungible'});470 });471 472 async function testChangesAccessRightsToProperty(mode: CollectionMode) {473 await usingApi(async api => {474 const collection = await createCollectionExpectSuccess({mode: mode});475 476 await expect(executeTransaction(477 api, 478 alice, 479 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true, collectionAdmin: true}}]), 480 )).to.not.be.rejected;481 482 await expect(executeTransaction(483 api, 484 alice, 485 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]), 486 )).to.not.be.rejected;487 488 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toHuman();489 expect(propertyRights).to.be.deep.equal([490 {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},491 ]);492 });493 }494 it('Changes access rights to properties of a NFT collection', async () => {495 await testChangesAccessRightsToProperty({type: 'NFT'});496 });497 it('Changes access rights to properties of a ReFungible collection', async function() {498 await requirePallets(this, [Pallets.ReFungible]);499500 await testChangesAccessRightsToProperty({type: 'ReFungible'});501 });502});503504describe('Negative Integration Test: Access Rights to Token Properties', () => {505 before(async () => {506 await usingApi(async (api, privateKeyWrapper) => {507 alice = privateKeyWrapper('//Alice');508 bob = privateKeyWrapper('//Bob');509 });510 });511512 async function testPreventsFromSettingAccessRightsNotAdminOrOwner(mode: CollectionMode) {513 await usingApi(async api => {514 const collection = await createCollectionExpectSuccess({mode: mode});515 516 await expect(executeTransaction(517 api, 518 bob, 519 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true, tokenOwner: true}}]), 520 )).to.be.rejectedWith(/common\.NoPermission/);521 522 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toJSON();523 expect(propertyRights).to.be.empty;524 });525 }526 it('Prevents from setting access rights to properties of a NFT collection if not an onwer/admin', async () => {527 await testPreventsFromSettingAccessRightsNotAdminOrOwner({type: 'NFT'});528 });529 it('Prevents from setting access rights to properties of a ReFungible collection if not an onwer/admin', async function() {530 await requirePallets(this, [Pallets.ReFungible]);531532 await testPreventsFromSettingAccessRightsNotAdminOrOwner({type: 'ReFungible'});533 });534535 async function testPreventFromAddingTooManyPossibleProperties(mode: CollectionMode) {536 await usingApi(async api => {537 const collection = await createCollectionExpectSuccess({mode: mode});538 539 const constitution = [];540 for (let i = 0; i < 65; i++) {541 constitution.push({542 key: 'property_' + i,543 permission: Math.random() > 0.5 ? {mutable: true, collectionAdmin: true, tokenOwner: true} : {},544 });545 }546 547 await expect(executeTransaction(548 api, 549 alice, 550 api.tx.unique.setTokenPropertyPermissions(collection, constitution), 551 )).to.be.rejectedWith(/common\.PropertyLimitReached/);552 553 const propertyRights = (await api.query.common.collectionPropertyPermissions(collection)).toJSON();554 expect(propertyRights).to.be.empty;555 }); 556 }557 it('Prevents from adding too many possible properties (NFT)', async () => {558 await testPreventFromAddingTooManyPossibleProperties({type: 'NFT'});559 });560 it('Prevents from adding too many possible properties (ReFungible)', async function() {561 await requirePallets(this, [Pallets.ReFungible]);562563 await testPreventFromAddingTooManyPossibleProperties({type: 'ReFungible'});564 });565566 async function testPreventAccessRightsModifiedIfConstant(mode: CollectionMode) {567 await usingApi(async api => {568 const collection = await createCollectionExpectSuccess({mode: mode});569 570 await expect(executeTransaction(571 api, 572 alice, 573 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]), 574 )).to.not.be.rejected;575 576 await expect(executeTransaction(577 api, 578 alice, 579 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'skullduggery', permission: {}}]), 580 )).to.be.rejectedWith(/common\.NoPermission/);581 582 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toHuman();583 expect(propertyRights).to.deep.equal([584 {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},585 ]);586 }); 587 }588 it('Prevents access rights to be modified if constant (NFT)', async () => {589 await testPreventAccessRightsModifiedIfConstant({type: 'NFT'});590 });591 it('Prevents access rights to be modified if constant (ReFungible)', async function() {592 await requirePallets(this, [Pallets.ReFungible]);593594 await testPreventAccessRightsModifiedIfConstant({type: 'ReFungible'});595 });596597 async function testPreventsAddingPropertiesWithInvalidNames(mode: CollectionMode) {598 await usingApi(async api => {599 const collection = await createCollectionExpectSuccess({mode: mode});600 601 const invalidProperties = [602 [{key: 'skullduggery', permission: {tokenOwner: true}}, {key: 'im possible', permission: {collectionAdmin: true}}],603 [{key: 'G#4', permission: {tokenOwner: true}}],604 [{key: 'HÆMILTON', permission: {mutable: false, collectionAdmin: true, tokenOwner: true}}],605 ];606 607 for (let i = 0; i < invalidProperties.length; i++) {608 await expect(executeTransaction(609 api, 610 alice, 611 api.tx.unique.setTokenPropertyPermissions(collection, invalidProperties[i]), 612 ), `on setting the new badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);613 }614 615 await expect(executeTransaction(616 api, 617 alice, 618 api.tx.unique.setTokenPropertyPermissions(collection, [{key: '', permission: {}}]), 619 ), 'on rejecting an unnamed property').to.be.rejectedWith(/common\.EmptyPropertyKey/);620 621 const correctKey = '--0x03116e387820CA05'; // PolkadotJS would parse this as an already encoded hex-string622 await expect(executeTransaction(623 api, 624 alice, 625 api.tx.unique.setTokenPropertyPermissions(collection, [626 {key: correctKey, permission: {collectionAdmin: true}},627 ]), 628 ), 'on setting the correctly-but-still-badly-named property').to.not.be.rejected;629 630 const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat(correctKey).concat('');631 632 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, keys)).toHuman();633 expect(propertyRights).to.be.deep.equal([634 {key: correctKey, permission: {mutable: false, collectionAdmin: true, tokenOwner: false}},635 ]);636 });637 }638 it('Prevents adding properties with invalid names (NFT)', async () => {639 await testPreventsAddingPropertiesWithInvalidNames({type: 'NFT'});640 });641 it('Prevents adding properties with invalid names (ReFungible)', async function() {642 await requirePallets(this, [Pallets.ReFungible]);643644 await testPreventsAddingPropertiesWithInvalidNames({type: 'ReFungible'});645 });646});647648// ---------- TOKEN PROPERTIES649650describe('Integration Test: Token Properties', () => {651 let permissions: {permission: any, signers: IKeyringPair[]}[];652653 before(async () => {654 await usingApi(async (api, privateKeyWrapper) => {655 alice = privateKeyWrapper('//Alice'); // collection owner656 bob = privateKeyWrapper('//Bob'); // collection admin657 charlie = privateKeyWrapper('//Charlie'); // token owner658659 permissions = [660 {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob]},661 {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob]},662 {permission: {mutable: true, tokenOwner: true}, signers: [charlie]},663 {permission: {mutable: false, tokenOwner: true}, signers: [charlie]},664 {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},665 {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},666 ];667 });668 });669 670 async function testReadsYetEmptyProperties(mode: CollectionMode) {671 await usingApi(async api => {672 const collection = await createCollectionExpectSuccess({mode: mode});673 const token = await createItemExpectSuccess(alice, collection, mode.type);674 675 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();676 expect(properties.map).to.be.empty;677 expect(properties.consumedSpace).to.be.equal(0);678 679 const tokenData = (await api.rpc.unique.tokenData(collection, token, ['anything'])).toJSON().properties;680 expect(tokenData).to.be.empty;681 });682 }683 it('Reads yet empty properties of a token (NFT)', async () => {684 await testReadsYetEmptyProperties({type: 'NFT'});685 });686 it('Reads yet empty properties of a token (ReFungible)', async function() {687 await requirePallets(this, [Pallets.ReFungible]);688689 await testReadsYetEmptyProperties({type: 'ReFungible'});690 });691692 async function testAssignPropertiesAccordingToPermissions(mode: CollectionMode, pieces: number) {693 await usingApi(async api => {694 const collection = await createCollectionExpectSuccess({mode: mode});695 const token = await createItemExpectSuccess(alice, collection, mode.type);696 await addCollectionAdminExpectSuccess(alice, collection, bob.address);697 await transferExpectSuccess(collection, token, alice, charlie, pieces, mode.type);698699 const propertyKeys: string[] = [];700 let i = 0;701 for (const permission of permissions) {702 for (const signer of permission.signers) {703 const key = i + '_' + signer.address;704 propertyKeys.push(key);705706 await expect(executeTransaction(707 api, 708 alice, 709 api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 710 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;711712 await expect(executeTransaction(713 api, 714 signer, 715 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 716 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;717 }718719 i++;720 }721722 const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toHuman() as any[];723 const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toHuman().properties as any[];724 for (let i = 0; i < properties.length; i++) {725 expect(properties[i].value).to.be.equal('Serotonin increase');726 expect(tokensData[i].value).to.be.equal('Serotonin increase');727 }728 });729 }730 it('Assigns properties to a token according to permissions (NFT)', async () => {731 await testAssignPropertiesAccordingToPermissions({type: 'NFT'}, 1);732 });733 it('Assigns properties to a token according to permissions (ReFungible)', async function() {734 await requirePallets(this, [Pallets.ReFungible]);735736 await testAssignPropertiesAccordingToPermissions({type: 'ReFungible'}, 100);737 });738739 async function testChangesPropertiesAccordingPermission(mode: CollectionMode, pieces: number) {740 await usingApi(async api => {741 const collection = await createCollectionExpectSuccess({mode: mode});742 const token = await createItemExpectSuccess(alice, collection, mode.type);743 await addCollectionAdminExpectSuccess(alice, collection, bob.address);744 await transferExpectSuccess(collection, token, alice, charlie, pieces, mode.type);745746 const propertyKeys: string[] = [];747 let i = 0;748 for (const permission of permissions) {749 if (!permission.permission.mutable) continue;750 751 for (const signer of permission.signers) {752 const key = i + '_' + signer.address;753 propertyKeys.push(key);754 755 await expect(executeTransaction(756 api, 757 alice, 758 api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 759 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;760 761 await expect(executeTransaction(762 api, 763 signer, 764 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 765 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;766 767 await expect(executeTransaction(768 api, 769 signer, 770 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin stable'}]), 771 ), `on changing property ${i} by ${signer.address}`).to.not.be.rejected;772 }773 774 i++;775 }776 777 const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toHuman() as any[];778 const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toHuman().properties as any[];779 for (let i = 0; i < properties.length; i++) {780 expect(properties[i].value).to.be.equal('Serotonin stable');781 expect(tokensData[i].value).to.be.equal('Serotonin stable');782 }783 });784 }785 it('Changes properties of a token according to permissions (NFT)', async () => {786 await testChangesPropertiesAccordingPermission({type: 'NFT'}, 1);787 });788 it('Changes properties of a token according to permissions (ReFungible)', async function() {789 await requirePallets(this, [Pallets.ReFungible]);790791 await testChangesPropertiesAccordingPermission({type: 'ReFungible'}, 100);792 });793794 async function testDeletePropertiesAccordingPermission(mode: CollectionMode, pieces: number) {795 await usingApi(async api => {796 const collection = await createCollectionExpectSuccess({mode: mode});797 const token = await createItemExpectSuccess(alice, collection, mode.type);798 await addCollectionAdminExpectSuccess(alice, collection, bob.address);799 await transferExpectSuccess(collection, token, alice, charlie, pieces, mode.type);800801 const propertyKeys: string[] = [];802 let i = 0;803 804 for (const permission of permissions) {805 if (!permission.permission.mutable) continue;806 807 for (const signer of permission.signers) {808 const key = i + '_' + signer.address;809 propertyKeys.push(key);810 811 await expect(executeTransaction(812 api, 813 alice, 814 api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 815 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;816 817 await expect(executeTransaction(818 api, 819 signer, 820 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 821 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;822 823 await expect(executeTransaction(824 api, 825 signer, 826 api.tx.unique.deleteTokenProperties(collection, token, [key]), 827 ), `on deleting property ${i} by ${signer.address}`).to.not.be.rejected;828 }829 830 i++;831 }832 833 const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toJSON() as any[];834 expect(properties).to.be.empty;835 const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toJSON().properties as any[];836 expect(tokensData).to.be.empty;837 expect((await api.query.nonfungible.tokenProperties(collection, token)).toJSON().consumedSpace).to.be.equal(0);838 });839 }840 it('Deletes properties of a token according to permissions (NFT)', async () => {841 await testDeletePropertiesAccordingPermission({type: 'NFT'}, 1);842 });843 it('Deletes properties of a token according to permissions (ReFungible)', async function() {844 await requirePallets(this, [Pallets.ReFungible]);845846 await testDeletePropertiesAccordingPermission({type: 'ReFungible'}, 100);847 });848849 it('Assigns properties to a nested token according to permissions', async () => {850 await usingApi(async api => {851 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});852 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});853 const token = await createItemExpectSuccess(alice, collection, 'NFT');854 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, token)});855 await addCollectionAdminExpectSuccess(alice, collection, bob.address);856 await transferExpectSuccess(collection, token, alice, charlie);857858 const propertyKeys: string[] = [];859 let i = 0;860 for (const permission of permissions) {861 for (const signer of permission.signers) {862 const key = i + '_' + signer.address;863 propertyKeys.push(key);864865 await expect(executeTransaction(866 api, 867 alice, 868 api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 869 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;870871 await expect(executeTransaction(872 api, 873 signer, 874 api.tx.unique.setTokenProperties(collection, nestedToken, [{key: key, value: 'Serotonin increase'}]), 875 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;876 }877878 i++;879 }880881 const properties = (await api.rpc.unique.tokenProperties(collection, nestedToken, propertyKeys)).toHuman() as any[];882 const tokensData = (await api.rpc.unique.tokenData(collection, nestedToken, propertyKeys)).toHuman().properties as any[];883 for (let i = 0; i < properties.length; i++) {884 expect(properties[i].value).to.be.equal('Serotonin increase');885 expect(tokensData[i].value).to.be.equal('Serotonin increase');886 }887 });888 });889890 it('Changes properties of a nested token according to permissions', async () => {891 await usingApi(async api => {892 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});893 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});894 const token = await createItemExpectSuccess(alice, collection, 'NFT');895 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, token)});896 await addCollectionAdminExpectSuccess(alice, collection, bob.address);897 await transferExpectSuccess(collection, token, alice, charlie);898899 const propertyKeys: string[] = [];900 let i = 0;901 for (const permission of permissions) {902 if (!permission.permission.mutable) continue;903 904 for (const signer of permission.signers) {905 const key = i + '_' + signer.address;906 propertyKeys.push(key);907908 await expect(executeTransaction(909 api, 910 alice, 911 api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 912 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;913914 await expect(executeTransaction(915 api, 916 signer, 917 api.tx.unique.setTokenProperties(collection, nestedToken, [{key: key, value: 'Serotonin increase'}]), 918 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;919920 await expect(executeTransaction(921 api, 922 signer, 923 api.tx.unique.setTokenProperties(collection, nestedToken, [{key: key, value: 'Serotonin stable'}]), 924 ), `on changing property ${i} by ${signer.address}`).to.not.be.rejected;925 }926927 i++;928 }929930 const properties = (await api.rpc.unique.tokenProperties(collection, nestedToken, propertyKeys)).toHuman() as any[];931 const tokensData = (await api.rpc.unique.tokenData(collection, nestedToken, propertyKeys)).toHuman().properties as any[];932 for (let i = 0; i < properties.length; i++) {933 expect(properties[i].value).to.be.equal('Serotonin stable');934 expect(tokensData[i].value).to.be.equal('Serotonin stable');935 }936 });937 });938939 it('Deletes properties of a nested token according to permissions', async () => {940 await usingApi(async api => {941 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});942 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});943 const token = await createItemExpectSuccess(alice, collection, 'NFT');944 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, token)});945 await addCollectionAdminExpectSuccess(alice, collection, bob.address);946 await transferExpectSuccess(collection, token, alice, charlie);947948 const propertyKeys: string[] = [];949 let i = 0;950951 for (const permission of permissions) {952 if (!permission.permission.mutable) continue;953 954 for (const signer of permission.signers) {955 const key = i + '_' + signer.address;956 propertyKeys.push(key);957958 await expect(executeTransaction(959 api, 960 alice, 961 api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 962 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;963964 await expect(executeTransaction(965 api, 966 signer, 967 api.tx.unique.setTokenProperties(collection, nestedToken, [{key: key, value: 'Serotonin increase'}]), 968 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;969970 await expect(executeTransaction(971 api, 972 signer, 973 api.tx.unique.deleteTokenProperties(collection, nestedToken, [key]), 974 ), `on deleting property ${i} by ${signer.address}`).to.not.be.rejected;975 }976 977 i++;978 }979980 const properties = (await api.rpc.unique.tokenProperties(collection, nestedToken, propertyKeys)).toJSON() as any[];981 expect(properties).to.be.empty;982 const tokensData = (await api.rpc.unique.tokenData(collection, nestedToken, propertyKeys)).toJSON().properties as any[];983 expect(tokensData).to.be.empty;984 expect((await api.query.nonfungible.tokenProperties(collection, nestedToken)).toJSON().consumedSpace).to.be.equal(0);985 });986 });987});988989describe('Negative Integration Test: Token Properties', () => {990 let collection: number;991 let token: number;992 let originalSpace: number;993 let constitution: {permission: any, signers: IKeyringPair[], sinner: IKeyringPair}[];994995 before(async () => {996 await usingApi(async (api, privateKeyWrapper) => {997 alice = privateKeyWrapper('//Alice');998 bob = privateKeyWrapper('//Bob');999 charlie = privateKeyWrapper('//Charlie');1000 const dave = privateKeyWrapper('//Dave');10011002 constitution = [1003 {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},1004 {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},1005 {permission: {mutable: true, tokenOwner: true}, signers: [charlie], sinner: alice},1006 {permission: {mutable: false, tokenOwner: true}, signers: [charlie], sinner: alice},1007 {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},1008 {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},1009 ];1010 });1011 });10121013 async function prepare(mode: CollectionMode, pieces: number) {1014 collection = await createCollectionExpectSuccess({mode: mode});1015 token = await createItemExpectSuccess(alice, collection, mode.type);1016 await addCollectionAdminExpectSuccess(alice, collection, bob.address);1017 await transferExpectSuccess(collection, token, alice, charlie, pieces, mode.type);1018 1019 await usingApi(async api => {1020 let i = 0;1021 for (const passage of constitution) {1022 const signer = passage.signers[0];1023 1024 await expect(executeTransaction(1025 api, 1026 alice, 1027 api.tx.unique.setTokenPropertyPermissions(collection, [{key: `${i}`, permission: passage.permission}]), 1028 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;1029 1030 await expect(executeTransaction(1031 api, 1032 signer, 1033 api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin increase'}]), 1034 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;1035 1036 i++;1037 }1038 1039 originalSpace = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON().consumedSpace as number;1040 }); 1041 }10421043 async function testForbidsChangingDeletingPropertiesUserOutsideOfPermissions(mode: CollectionMode, pieces: number) {1044 await prepare(mode, pieces);1045 1046 await usingApi(async api => {1047 let i = -1;1048 for (const forbiddance of constitution) {1049 i++;1050 if (!forbiddance.permission.mutable) continue;1051 1052 await expect(executeTransaction(1053 api, 1054 forbiddance.sinner, 1055 api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin down'}]), 1056 ), `on failing to change property ${i} by ${forbiddance.sinner.address}`).to.be.rejectedWith(/common\.NoPermission/);1057 1058 await expect(executeTransaction(1059 api, 1060 forbiddance.sinner, 1061 api.tx.unique.deleteTokenProperties(collection, token, [`${i}`]), 1062 ), `on failing to delete property ${i} by ${forbiddance.sinner.address}`).to.be.rejectedWith(/common\.NoPermission/);1063 }1064 1065 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();1066 expect(properties.consumedSpace).to.be.equal(originalSpace);1067 });1068 }1069 it('Forbids changing/deleting properties of a token if the user is outside of permissions (NFT)', async () => {1070 await testForbidsChangingDeletingPropertiesUserOutsideOfPermissions({type: 'NFT'}, 1);1071 });1072 it('Forbids changing/deleting properties of a token if the user is outside of permissions (ReFungible)', async function() {1073 await requirePallets(this, [Pallets.ReFungible]);10741075 await testForbidsChangingDeletingPropertiesUserOutsideOfPermissions({type: 'ReFungible'}, 100);1076 });10771078 async function testForbidsChangingDeletingPropertiesIfPropertyImmutable(mode: CollectionMode, pieces: number) {1079 await prepare(mode, pieces);1080 1081 await usingApi(async api => {1082 let i = -1;1083 for (const permission of constitution) {1084 i++;1085 if (permission.permission.mutable) continue;1086 1087 await expect(executeTransaction(1088 api, 1089 permission.signers[0], 1090 api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin down'}]), 1091 ), `on failing to change property ${i} by ${permission.signers[0].address}`).to.be.rejectedWith(/common\.NoPermission/);1092 1093 await expect(executeTransaction(1094 api, 1095 permission.signers[0], 1096 api.tx.unique.deleteTokenProperties(collection, token, [i.toString()]), 1097 ), `on failing to delete property ${i} by ${permission.signers[0].address}`).to.be.rejectedWith(/common\.NoPermission/);1098 }1099 1100 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();1101 expect(properties.consumedSpace).to.be.equal(originalSpace);1102 }); 1103 }1104 it('Forbids changing/deleting properties of a token if the property is permanent (immutable) (NFT)', async () => {1105 await testForbidsChangingDeletingPropertiesIfPropertyImmutable({type: 'NFT'}, 1);1106 });1107 it('Forbids changing/deleting properties of a token if the property is permanent (immutable) (ReFungible)', async function() {1108 await requirePallets(this, [Pallets.ReFungible]);11091110 await testForbidsChangingDeletingPropertiesIfPropertyImmutable({type: 'ReFungible'}, 100);1111 });11121113 async function testForbidsAddingPropertiesIfPropertyNotDeclared(mode: CollectionMode, pieces: number) {1114 await prepare(mode, pieces);11151116 await usingApi(async api => {1117 await expect(executeTransaction(1118 api, 1119 alice, 1120 api.tx.unique.setTokenProperties(collection, token, [{key: 'non-existent', value: 'I exist!'}]), 1121 ), 'on failing to add a previously non-existent property').to.be.rejectedWith(/common\.NoPermission/);1122 1123 await expect(executeTransaction(1124 api, 1125 alice, 1126 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'now-existent', permission: {}}]), 1127 ), 'on setting a new non-permitted property').to.not.be.rejected;1128 1129 await expect(executeTransaction(1130 api, 1131 alice, 1132 api.tx.unique.setTokenProperties(collection, token, [{key: 'now-existent', value: 'I exist!'}]), 1133 ), 'on failing to add a property forbidden by the \'None\' permission').to.be.rejectedWith(/common\.NoPermission/);1134 1135 expect((await api.rpc.unique.tokenProperties(collection, token, ['non-existent', 'now-existent'])).toJSON()).to.be.empty;1136 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();1137 expect(properties.consumedSpace).to.be.equal(originalSpace);1138 });1139 }1140 it('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission (NFT)', async () => {1141 await testForbidsAddingPropertiesIfPropertyNotDeclared({type: 'NFT'}, 1);1142 });1143 it('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission (ReFungible)', async function() {1144 await requirePallets(this, [Pallets.ReFungible]);11451146 await testForbidsAddingPropertiesIfPropertyNotDeclared({type: 'ReFungible'}, 100);1147 });11481149 async function testForbidsAddingTooManyProperties(mode: CollectionMode, pieces: number) {1150 await prepare(mode, pieces);11511152 await usingApi(async api => {1153 await expect(executeTransaction(1154 api, 1155 alice, 1156 api.tx.unique.setTokenPropertyPermissions(collection, [1157 {key: 'a_holy_book', permission: {collectionAdmin: true, tokenOwner: true}}, 1158 {key: 'young_years', permission: {collectionAdmin: true, tokenOwner: true}},1159 ]), 1160 ), 'on setting a new non-permitted property').to.not.be.rejected;1161 1162 // Mute the general tx parsing error1163 {1164 console.error = () => {};1165 await expect(executeTransaction(1166 api, 1167 alice, 1168 api.tx.unique.setCollectionProperties(collection, [{key: 'a_holy_book', value: 'word '.repeat(6554)}]), 1169 )).to.be.rejected;1170 }1171 1172 await expect(executeTransaction(1173 api, 1174 alice, 1175 api.tx.unique.setTokenProperties(collection, token, [1176 {key: 'a_holy_book', value: 'word '.repeat(3277)}, 1177 {key: 'young_years', value: 'neverending'.repeat(1490)},1178 ]), 1179 )).to.be.rejectedWith(/common\.NoSpaceForProperty/);1180 1181 expect((await api.rpc.unique.tokenProperties(collection, token, ['a_holy_book', 'young_years'])).toJSON()).to.be.empty;1182 const propertiesMap = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();1183 expect(propertiesMap.consumedSpace).to.be.equal(originalSpace);1184 });1185 }1186 it('Forbids adding too many properties to a token (NFT)', async () => {1187 await testForbidsAddingTooManyProperties({type: 'NFT'}, 1);1188 });1189 it('Forbids adding too many properties to a token (ReFungible)', async function() {1190 await requirePallets(this, [Pallets.ReFungible]);11911192 await testForbidsAddingTooManyProperties({type: 'ReFungible'}, 100);1193 });1194});11951196describe('ReFungible token properties permissions tests', () => {1197 let collection: number;1198 let token: number;11991200 before(async function() {1201 await requirePallets(this, [Pallets.ReFungible]);12021203 await usingApi(async (api, privateKeyWrapper) => {1204 alice = privateKeyWrapper('//Alice');1205 bob = privateKeyWrapper('//Bob');1206 charlie = privateKeyWrapper('//Charlie');1207 });1208 });12091210 beforeEach(async () => {1211 await usingApi(async api => {1212 collection = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});1213 token = await createItemExpectSuccess(alice, collection, 'ReFungible');1214 await addCollectionAdminExpectSuccess(alice, collection, bob.address);12151216 await expect(executeTransaction(1217 api, 1218 alice, 1219 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'key', permission: {mutable:true, tokenOwner: true}}]), 1220 )).to.not.be.rejected;1221 });1222 });12231224 it('Forbids add token property with tokenOwher==true but signer have\'t all pieces', async () => {1225 await usingApi(async api => {1226 await transferExpectSuccess(collection, token, alice, charlie, 33, 'ReFungible');1227 1228 await expect(executeTransaction(1229 api, 1230 alice, 1231 api.tx.unique.setTokenProperties(collection, token, [1232 {key: 'key', value: 'word'}, 1233 ]), 1234 )).to.be.rejectedWith(/common\.NoPermission/);1235 });1236 });12371238 it('Forbids mutate token property with tokenOwher==true but signer have\'t all pieces', async () => {1239 await usingApi(async api => {1240 await expect(executeTransaction(1241 api, 1242 alice, 1243 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'key', permission: {mutable:true, tokenOwner: true}}]), 1244 )).to.not.be.rejected;1245 1246 await expect(executeTransaction(1247 api, 1248 alice, 1249 api.tx.unique.setTokenProperties(collection, token, [1250 {key: 'key', value: 'word'}, 1251 ]), 1252 )).to.be.not.rejected;12531254 await transferExpectSuccess(collection, token, alice, charlie, 33, 'ReFungible');1255 1256 await expect(executeTransaction(1257 api, 1258 alice, 1259 api.tx.unique.setTokenProperties(collection, token, [1260 {key: 'key', value: 'bad word'}, 1261 ]), 1262 )).to.be.rejectedWith(/common\.NoPermission/);1263 });1264 });12651266 it('Forbids delete token property with tokenOwher==true but signer have\'t all pieces', async () => {1267 await usingApi(async api => {1268 await expect(executeTransaction(1269 api, 1270 alice, 1271 api.tx.unique.setTokenProperties(collection, token, [1272 {key: 'key', value: 'word'}, 1273 ]), 1274 )).to.be.not.rejected;12751276 await transferExpectSuccess(collection, token, alice, charlie, 33, 'ReFungible');1277 1278 await expect(executeTransaction(1279 api, 1280 alice, 1281 api.tx.unique.deleteTokenProperties(collection, token, [1282 'key',1283 ]), 1284 )).to.be.rejectedWith(/common\.NoPermission/);1285 });1286 });1287});1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {IKeyringPair} from '@polkadot/types/types';18import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from '../util/playgrounds';19import {UniqueHelper, UniqueBaseCollection, UniqueNFTCollection, UniqueNFToken, UniqueRFTCollection, UniqueRFToken} from '../util/playgrounds/unique';2021// ---------- COLLECTION PROPERTIES2223describe('Integration Test: Collection Properties', () => {24 let alice: IKeyringPair;25 let bob: IKeyringPair;2627 before(async () => {28 await usingPlaygrounds(async (helper, privateKey) => {29 const donor = privateKey('//Alice');30 [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor);31 });32 });3334 itSub('Properties are initially empty', async ({helper}) => {35 const collection = await helper.nft.mintCollection(alice);36 expect(await collection.getProperties()).to.be.empty;37 });3839 async function testSetsPropertiesForCollection(collection: UniqueBaseCollection) {40 // As owner41 await expect(collection.setProperties(alice, [{key: 'electron', value: 'come bond'}])).to.be.fulfilled;4243 await collection.addAdmin(alice, {Substrate: bob.address});4445 // As administrator46 await expect(collection.setProperties(bob, [{key: 'black_hole'}])).to.be.fulfilled;4748 const properties = await collection.getProperties();49 expect(properties).to.include.deep.members([50 {key: 'electron', value: 'come bond'},51 {key: 'black_hole', value: ''},52 ]);53 }5455 itSub('Sets properties for a NFT collection', async ({helper}) => {56 await testSetsPropertiesForCollection(await helper.nft.mintCollection(alice));57 });5859 itSub.ifWithPallets('Sets properties for a ReFungible collection', [Pallets.ReFungible], async ({helper}) => {60 await testSetsPropertiesForCollection(await helper.rft.mintCollection(alice));61 });6263 async function testCheckValidNames(collection: UniqueBaseCollection) {64 // alpha symbols65 await expect(collection.setProperties(alice, [{key: 'answer'}])).to.be.fulfilled;6667 // numeric symbols68 await expect(collection.setProperties(alice, [{key: '451'}])).to.be.fulfilled;6970 // underscore symbol71 await expect(collection.setProperties(alice, [{key: 'black_hole'}])).to.be.fulfilled;7273 // dash symbol74 await expect(collection.setProperties(alice, [{key: '-'}])).to.be.fulfilled;7576 // dot symbol77 await expect(collection.setProperties(alice, [{key: 'once.in.a.long.long.while...', value: 'you get a little lost'}])).to.be.fulfilled;7879 const properties = await collection.getProperties();80 expect(properties).to.include.deep.members([81 {key: 'answer', value: ''},82 {key: '451', value: ''},83 {key: 'black_hole', value: ''},84 {key: '-', value: ''},85 {key: 'once.in.a.long.long.while...', value: 'you get a little lost'},86 ]);87 }8889 itSub('Check valid names for NFT collection properties keys', async ({helper}) => {90 await testCheckValidNames(await helper.nft.mintCollection(alice));91 });9293 itSub.ifWithPallets('Check valid names for ReFungible collection properties keys', [Pallets.ReFungible], async ({helper}) => {94 await testCheckValidNames(await helper.rft.mintCollection(alice));95 });9697 async function testChangesProperties(collection: UniqueBaseCollection) {98 await expect(collection.setProperties(alice, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: ''}])).to.be.fulfilled;99100 // Mutate the properties101 await expect(collection.setProperties(alice, [{key: 'black_hole', value: 'LIGO'}])).to.be.fulfilled;102103 const properties = await collection.getProperties();104 expect(properties).to.include.deep.members([105 {key: 'electron', value: 'come bond'},106 {key: 'black_hole', value: 'LIGO'},107 ]);108 }109110 itSub('Changes properties of a NFT collection', async ({helper}) => {111 await testChangesProperties(await helper.nft.mintCollection(alice));112 });113114 itSub.ifWithPallets('Changes properties of a ReFungible collection', [Pallets.ReFungible], async ({helper}) => {115 await testChangesProperties(await helper.rft.mintCollection(alice));116 });117118 async function testDeleteProperties(collection: UniqueBaseCollection) {119 await expect(collection.setProperties(alice, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}])).to.be.fulfilled;120121 await expect(collection.deleteProperties(alice, ['electron'])).to.be.fulfilled;122123 const properties = await collection.getProperties(['black_hole', 'electron']);124 expect(properties).to.be.deep.equal([125 {key: 'black_hole', value: 'LIGO'},126 ]);127 }128129 itSub('Deletes properties of a NFT collection', async ({helper}) => {130 await testDeleteProperties(await helper.nft.mintCollection(alice));131 });132133 itSub.ifWithPallets('Deletes properties of a ReFungible collection', [Pallets.ReFungible], async ({helper}) => {134 await testDeleteProperties(await helper.rft.mintCollection(alice));135 });136});137138describe('Negative Integration Test: Collection Properties', () => {139 let alice: IKeyringPair;140 let bob: IKeyringPair;141142 before(async () => {143 await usingPlaygrounds(async (helper, privateKey) => {144 const donor = privateKey('//Alice');145 [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor);146 });147 });148 149 async function testFailsSetPropertiesIfNotOwnerOrAdmin(collection: UniqueBaseCollection) { 150 await expect(collection.setProperties(bob, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]))151 .to.be.rejectedWith(/common\.NoPermission/);152153 expect(await collection.getProperties()).to.be.empty;154 }155156 itSub('Fails to set properties in a NFT collection if not its onwer/administrator', async ({helper}) => {157 await testFailsSetPropertiesIfNotOwnerOrAdmin(await helper.nft.mintCollection(alice));158 });159160 itSub.ifWithPallets('Fails to set properties in a ReFungible collection if not its onwer/administrator', [Pallets.ReFungible], async ({helper}) => {161 await testFailsSetPropertiesIfNotOwnerOrAdmin(await helper.rft.mintCollection(alice));162 });163 164 async function testFailsSetPropertiesThatExeedLimits(collection: UniqueBaseCollection) {165 const spaceLimit = (await (collection.helper!.api! as any).query.common.collectionProperties(collection.collectionId)).spaceLimit.toNumber();166 167 // Mute the general tx parsing error, too many bytes to process168 {169 console.error = () => {};170 await expect(collection.setProperties(alice, [171 {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 9))},172 ])).to.be.rejected;173 }174175 expect(await collection.getProperties(['electron'])).to.be.empty;176177 await expect(collection.setProperties(alice, [178 {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 18))}, 179 {key: 'black_hole', value: '0'.repeat(Math.ceil(spaceLimit! / 2))}, 180 ])).to.be.rejectedWith(/common\.NoSpaceForProperty/);181182 expect(await collection.getProperties(['electron', 'black_hole'])).to.be.empty;183 }184185 itSub('Fails to set properties that exceed the limits (NFT)', async ({helper}) => {186 await testFailsSetPropertiesThatExeedLimits(await helper.nft.mintCollection(alice));187 });188189 itSub.ifWithPallets('Fails to set properties that exceed the limits (ReFungible)', [Pallets.ReFungible], async ({helper}) => {190 await testFailsSetPropertiesThatExeedLimits(await helper.rft.mintCollection(alice));191 });192 193 async function testFailsSetMorePropertiesThanAllowed(collection: UniqueBaseCollection) {194 const propertiesToBeSet = [];195 for (let i = 0; i < 65; i++) {196 propertiesToBeSet.push({197 key: 'electron_' + i,198 value: Math.random() > 0.5 ? 'high' : 'low',199 });200 }201202 await expect(collection.setProperties(alice, propertiesToBeSet)).203 to.be.rejectedWith(/common\.PropertyLimitReached/);204205 expect(await collection.getProperties()).to.be.empty;206 }207208 itSub('Fails to set more properties than it is allowed (NFT)', async ({helper}) => {209 await testFailsSetMorePropertiesThanAllowed(await helper.nft.mintCollection(alice));210 });211212 itSub.ifWithPallets('Fails to set more properties than it is allowed (ReFungible)', [Pallets.ReFungible], async ({helper}) => {213 await testFailsSetMorePropertiesThanAllowed(await helper.rft.mintCollection(alice));214 });215 216 async function testFailsSetPropertiesWithInvalidNames(collection: UniqueBaseCollection) {217 const invalidProperties = [218 [{key: 'electron', value: 'negative'}, {key: 'string theory', value: 'understandable'}],219 [{key: 'Mr/Sandman', value: 'Bring me a gene'}],220 [{key: 'déjà vu', value: 'hmm...'}],221 ];222223 for (let i = 0; i < invalidProperties.length; i++) {224 await expect(225 collection.setProperties(alice, invalidProperties[i]), 226 `on rejecting the new badly-named property #${i}`,227 ).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);228 }229230 await expect(231 collection.setProperties(alice, [{key: '', value: 'nothing must not exist'}]), 232 'on rejecting an unnamed property',233 ).to.be.rejectedWith(/common\.EmptyPropertyKey/);234235 await expect(236 collection.setProperties(alice, [{key: 'CRISPR-Cas9', value: 'rewriting nature!'}]), 237 'on setting the correctly-but-still-badly-named property',238 ).to.be.fulfilled;239240 const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat('CRISPR-Cas9').concat('');241242 const properties = await collection.getProperties(keys);243 expect(properties).to.be.deep.equal([244 {key: 'CRISPR-Cas9', value: 'rewriting nature!'},245 ]);246247 for (let i = 0; i < invalidProperties.length; i++) {248 await expect(249 collection.deleteProperties(alice, invalidProperties[i].map(propertySet => propertySet.key)), 250 `on trying to delete the non-existent badly-named property #${i}`,251 ).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);252 }253 }254255 itSub('Fails to set properties with invalid names (NFT)', async ({helper}) => {256 await testFailsSetPropertiesWithInvalidNames(await helper.nft.mintCollection(alice));257 });258259 itSub.ifWithPallets('Fails to set properties with invalid names (ReFungible)', [Pallets.ReFungible], async ({helper}) => {260 await testFailsSetPropertiesWithInvalidNames(await helper.rft.mintCollection(alice));261 });262});263264// ---------- ACCESS RIGHTS265266describe('Integration Test: Access Rights to Token Properties', () => {267 let alice: IKeyringPair;268 let bob: IKeyringPair;269270 before(async () => {271 await usingPlaygrounds(async (helper, privateKey) => {272 const donor = privateKey('//Alice');273 [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor);274 });275 });276 277 itSub('Reads access rights to properties of a collection', async ({helper}) => {278 const collection = await helper.nft.mintCollection(alice);279 const propertyRights = (await helper.api!.query.common.collectionPropertyPermissions(collection.collectionId)).toJSON();280 expect(propertyRights).to.be.empty;281 });282 283 async function testSetsAccessRightsToProperties(collection: UniqueNFTCollection | UniqueRFTCollection) { 284 await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: true}}]))285 .to.be.fulfilled;286287 await collection.addAdmin(alice, {Substrate: bob.address});288289 await expect(collection.setTokenPropertyPermissions(bob, [{key: 'mindgame', permission: {collectionAdmin: true, tokenOwner: false}}]))290 .to.be.fulfilled;291292 const propertyRights = await collection.getPropertyPermissions(['skullduggery', 'mindgame']);293 expect(propertyRights).to.include.deep.members([294 {key: 'skullduggery', permission: {mutable: true, collectionAdmin: false, tokenOwner: false}},295 {key: 'mindgame', permission: {mutable: false, collectionAdmin: true, tokenOwner: false}},296 ]);297 }298299 itSub('Sets access rights to properties of a collection (NFT)', async ({helper}) => {300 await testSetsAccessRightsToProperties(await helper.nft.mintCollection(alice));301 });302303 itSub.ifWithPallets('Sets access rights to properties of a collection (ReFungible)', [Pallets.ReFungible], async ({helper}) => {304 await testSetsAccessRightsToProperties(await helper.rft.mintCollection(alice));305 });306 307 async function testChangesAccessRightsToProperty(collection: UniqueNFTCollection | UniqueRFTCollection) {308 await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: true, collectionAdmin: true}}]))309 .to.be.fulfilled;310311 await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]))312 .to.be.fulfilled;313314 const propertyRights = await collection.getPropertyPermissions();315 expect(propertyRights).to.be.deep.equal([316 {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},317 ]);318 }319320 itSub('Changes access rights to properties of a NFT collection', async ({helper}) => {321 await testChangesAccessRightsToProperty(await helper.nft.mintCollection(alice));322 });323324 itSub.ifWithPallets('Changes access rights to properties of a ReFungible collection', [Pallets.ReFungible], async ({helper}) => {325 await testChangesAccessRightsToProperty(await helper.rft.mintCollection(alice));326 });327});328329describe('Negative Integration Test: Access Rights to Token Properties', () => {330 let alice: IKeyringPair;331 let bob: IKeyringPair;332333 before(async () => {334 await usingPlaygrounds(async (helper, privateKey) => {335 const donor = privateKey('//Alice');336 [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor);337 });338 });339340 async function testPreventsFromSettingAccessRightsNotAdminOrOwner(collection: UniqueNFTCollection | UniqueRFTCollection) {341 await expect(collection.setTokenPropertyPermissions(bob, [{key: 'skullduggery', permission: {mutable: true, tokenOwner: true}}]))342 .to.be.rejectedWith(/common\.NoPermission/);343344 const propertyRights = await collection.getPropertyPermissions(['skullduggery']);345 expect(propertyRights).to.be.empty;346 }347348 itSub('Prevents from setting access rights to properties of a NFT collection if not an onwer/admin', async ({helper}) => {349 await testPreventsFromSettingAccessRightsNotAdminOrOwner(await helper.nft.mintCollection(alice));350 });351352 itSub.ifWithPallets('Prevents from setting access rights to properties of a ReFungible collection if not an onwer/admin', [Pallets.ReFungible], async ({helper}) => {353 await testPreventsFromSettingAccessRightsNotAdminOrOwner(await helper.rft.mintCollection(alice));354 });355356 async function testPreventFromAddingTooManyPossibleProperties(collection: UniqueNFTCollection | UniqueRFTCollection) { 357 const constitution = [];358 for (let i = 0; i < 65; i++) {359 constitution.push({360 key: 'property_' + i,361 permission: Math.random() > 0.5 ? {mutable: true, collectionAdmin: true, tokenOwner: true} : {},362 });363 }364365 await expect(collection.setTokenPropertyPermissions(alice, constitution))366 .to.be.rejectedWith(/common\.PropertyLimitReached/);367368 const propertyRights = await collection.getPropertyPermissions();369 expect(propertyRights).to.be.empty;370 }371372 itSub('Prevents from adding too many possible properties (NFT)', async ({helper}) => {373 await testPreventFromAddingTooManyPossibleProperties(await helper.nft.mintCollection(alice));374 });375376 itSub.ifWithPallets('Prevents from adding too many possible properties (ReFungible)', [Pallets.ReFungible], async ({helper}) => {377 await testPreventFromAddingTooManyPossibleProperties(await helper.rft.mintCollection(alice));378 });379380 async function testPreventAccessRightsModifiedIfConstant(collection: UniqueNFTCollection | UniqueRFTCollection) {381 await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]))382 .to.be.fulfilled;383384 await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {collectionAdmin: true}}]))385 .to.be.rejectedWith(/common\.NoPermission/);386387 const propertyRights = await collection.getPropertyPermissions(['skullduggery']);388 expect(propertyRights).to.deep.equal([389 {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},390 ]);391 }392393 itSub('Prevents access rights to be modified if constant (NFT)', async ({helper}) => {394 await testPreventAccessRightsModifiedIfConstant(await helper.nft.mintCollection(alice));395 });396397 itSub.ifWithPallets('Prevents access rights to be modified if constant (ReFungible)', [Pallets.ReFungible], async ({helper}) => {398 await testPreventAccessRightsModifiedIfConstant(await helper.rft.mintCollection(alice));399 });400401 async function testPreventsAddingPropertiesWithInvalidNames(collection: UniqueNFTCollection | UniqueRFTCollection) {402 const invalidProperties = [403 [{key: 'skullduggery', permission: {tokenOwner: true}}, {key: 'im possible', permission: {collectionAdmin: true}}],404 [{key: 'G#4', permission: {tokenOwner: true}}],405 [{key: 'HÆMILTON', permission: {mutable: false, collectionAdmin: true, tokenOwner: true}}],406 ];407408 for (let i = 0; i < invalidProperties.length; i++) {409 await expect(410 collection.setTokenPropertyPermissions(alice, invalidProperties[i]), 411 `on setting the new badly-named property #${i}`,412 ).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);413 }414415 await expect(416 collection.setTokenPropertyPermissions(alice, [{key: '', permission: {}}]), 417 'on rejecting an unnamed property',418 ).to.be.rejectedWith(/common\.EmptyPropertyKey/);419420 const correctKey = '--0x03116e387820CA05'; // PolkadotJS would parse this as an already encoded hex-string421 await expect(422 collection.setTokenPropertyPermissions(alice, [423 {key: correctKey, permission: {collectionAdmin: true}},424 ]), 425 'on setting the correctly-but-still-badly-named property',426 ).to.be.fulfilled;427428 const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat(correctKey).concat('');429430 const propertyRights = await collection.getPropertyPermissions(keys);431 expect(propertyRights).to.be.deep.equal([432 {key: correctKey, permission: {mutable: false, collectionAdmin: true, tokenOwner: false}},433 ]);434 }435436 itSub('Prevents adding properties with invalid names (NFT)', async ({helper}) => {437 await testPreventsAddingPropertiesWithInvalidNames(await helper.nft.mintCollection(alice));438 });439440 itSub.ifWithPallets('Prevents adding properties with invalid names (ReFungible)', [Pallets.ReFungible], async ({helper}) => {441 await testPreventsAddingPropertiesWithInvalidNames(await helper.rft.mintCollection(alice));442 });443});444445// ---------- TOKEN PROPERTIES446447describe('Integration Test: Token Properties', () => {448 let alice: IKeyringPair; // collection owner449 let bob: IKeyringPair; // collection admin450 let charlie: IKeyringPair; // token owner451452 let permissions: {permission: any, signers: IKeyringPair[]}[];453454 before(async () => {455 await usingPlaygrounds(async (helper, privateKey) => {456 const donor = privateKey('//Alice');457 [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);458 });459460 // todo:playgrounds probably separate these tests later461 permissions = [462 {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob]},463 {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob]},464 {permission: {mutable: true, tokenOwner: true}, signers: [charlie]},465 {permission: {mutable: false, tokenOwner: true}, signers: [charlie]},466 {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},467 {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},468 ];469 });470 471 async function testReadsYetEmptyProperties(token: UniqueNFToken | UniqueRFToken) {472 const properties = await token.getProperties();473 expect(properties).to.be.empty;474475 const tokenData = await token.getData();476 expect(tokenData!.properties).to.be.empty;477 }478479 itSub('Reads yet empty properties of a token (NFT)', async ({helper}) => {480 const collection = await helper.nft.mintCollection(alice);481 const token = await collection.mintToken(alice);482 await testReadsYetEmptyProperties(token);483 });484485 itSub.ifWithPallets('Reads yet empty properties of a token (ReFungible)', [Pallets.ReFungible], async ({helper}) => {486 const collection = await helper.rft.mintCollection(alice);487 const token = await collection.mintToken(alice);488 await testReadsYetEmptyProperties(token);489 });490491 async function testAssignPropertiesAccordingToPermissions(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {492 await token.collection.addAdmin(alice, {Substrate: bob.address});493 await token.transfer(alice, {Substrate: charlie.address}, pieces);494495 const propertyKeys: string[] = [];496 let i = 0;497 for (const permission of permissions) {498 i++;499 let j = 0;500 for (const signer of permission.signers) {501 j++;502 const key = i + '_' + signer.address;503 propertyKeys.push(key);504505 await expect(506 token.collection.setTokenPropertyPermissions(alice, [{key: key, permission: permission.permission}]), 507 `on setting permission #${i} by alice`,508 ).to.be.fulfilled;509510 await expect(511 token.setProperties(signer, [{key: key, value: 'Serotonin increase'}]), 512 `on adding property #${i} by signer #${j}`,513 ).to.be.fulfilled;514 }515 }516517 const properties = await token.getProperties(propertyKeys);518 const tokenData = await token.getData();519 for (let i = 0; i < properties.length; i++) {520 expect(properties[i].value).to.be.equal('Serotonin increase');521 expect(tokenData!.properties[i].value).to.be.equal('Serotonin increase');522 }523 }524525 itSub('Assigns properties to a token according to permissions (NFT)', async ({helper}) => {526 const collection = await helper.nft.mintCollection(alice);527 const token = await collection.mintToken(alice);528 await testAssignPropertiesAccordingToPermissions(token, 1n);529 });530531 itSub.ifWithPallets('Assigns properties to a token according to permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => {532 const collection = await helper.rft.mintCollection(alice);533 const token = await collection.mintToken(alice, 100n);534 await testAssignPropertiesAccordingToPermissions(token, 100n);535 });536537 async function testChangesPropertiesAccordingPermission(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {538 await token.collection.addAdmin(alice, {Substrate: bob.address});539 await token.transfer(alice, {Substrate: charlie.address}, pieces);540541 const propertyKeys: string[] = [];542 let i = 0;543 for (const permission of permissions) {544 i++;545 if (!permission.permission.mutable) continue;546 547 let j = 0;548 for (const signer of permission.signers) {549 j++;550 const key = i + '_' + signer.address;551 propertyKeys.push(key);552553 await expect(554 token.collection.setTokenPropertyPermissions(alice, [{key: key, permission: permission.permission}]), 555 `on setting permission #${i} by alice`,556 ).to.be.fulfilled;557558 await expect(559 token.setProperties(signer, [{key, value: 'Serotonin increase'}]), 560 `on adding property #${i} by signer #${j}`,561 ).to.be.fulfilled;562563 await expect(564 token.setProperties(signer, [{key, value: 'Serotonin stable'}]), 565 `on changing property #${i} by signer #${j}`,566 ).to.be.fulfilled;567 }568 }569570 const properties = await token.getProperties(propertyKeys);571 const tokenData = await token.getData();572 for (let i = 0; i < properties.length; i++) {573 expect(properties[i].value).to.be.equal('Serotonin stable');574 expect(tokenData!.properties[i].value).to.be.equal('Serotonin stable');575 }576 }577578 itSub('Changes properties of a token according to permissions (NFT)', async ({helper}) => {579 const collection = await helper.nft.mintCollection(alice);580 const token = await collection.mintToken(alice);581 await testChangesPropertiesAccordingPermission(token, 1n);582 });583584 itSub.ifWithPallets('Changes properties of a token according to permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => {585 const collection = await helper.rft.mintCollection(alice);586 const token = await collection.mintToken(alice, 100n);587 await testChangesPropertiesAccordingPermission(token, 100n);588 });589590 async function testDeletePropertiesAccordingPermission(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {591 await token.collection.addAdmin(alice, {Substrate: bob.address});592 await token.transfer(alice, {Substrate: charlie.address}, pieces);593594 const propertyKeys: string[] = [];595 let i = 0;596597 for (const permission of permissions) {598 i++;599 if (!permission.permission.mutable) continue;600 601 let j = 0;602 for (const signer of permission.signers) {603 j++;604 const key = i + '_' + signer.address;605 propertyKeys.push(key);606607 await expect(608 token.collection.setTokenPropertyPermissions(alice, [{key: key, permission: permission.permission}]), 609 `on setting permission #${i} by alice`,610 ).to.be.fulfilled;611612 await expect(613 token.setProperties(signer, [{key, value: 'Serotonin increase'}]), 614 `on adding property #${i} by signer #${j}`,615 ).to.be.fulfilled;616617 await expect(618 token.deleteProperties(signer, [key]), 619 `on deleting property #${i} by signer #${j}`,620 ).to.be.fulfilled;621 }622 }623624 expect(await token.getProperties(propertyKeys)).to.be.empty;625 expect((await token.getData())!.properties).to.be.empty;626 }627 628 itSub('Deletes properties of a token according to permissions (NFT)', async ({helper}) => {629 const collection = await helper.nft.mintCollection(alice);630 const token = await collection.mintToken(alice);631 await testDeletePropertiesAccordingPermission(token, 1n);632 });633634 itSub.ifWithPallets('Deletes properties of a token according to permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => {635 const collection = await helper.rft.mintCollection(alice);636 const token = await collection.mintToken(alice, 100n);637 await testDeletePropertiesAccordingPermission(token, 100n);638 });639640 itSub('Assigns properties to a nested token according to permissions', async ({helper}) => {641 const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});642 const collectionB = await helper.nft.mintCollection(alice);643 const targetToken = await collectionA.mintToken(alice);644 const nestedToken = await collectionB.mintToken(alice, targetToken.nestingAccount());645646 await collectionB.addAdmin(alice, {Substrate: bob.address});647 await targetToken.transfer(alice, {Substrate: charlie.address});648649 const propertyKeys: string[] = [];650 let i = 0;651 for (const permission of permissions) {652 i++;653 let j = 0;654 for (const signer of permission.signers) {655 j++;656 const key = i + '_' + signer.address;657 propertyKeys.push(key);658 659 await expect(660 nestedToken.collection.setTokenPropertyPermissions(alice, [{key: key, permission: permission.permission}]), 661 `on setting permission #${i} by alice`,662 ).to.be.fulfilled;663664 await expect(665 nestedToken.setProperties(signer, [{key, value: 'Serotonin increase'}]), 666 `on adding property #${i} by signer #${j}`,667 ).to.be.fulfilled;668 }669 }670671 const properties = await nestedToken.getProperties(propertyKeys);672 const tokenData = await nestedToken.getData();673 for (let i = 0; i < properties.length; i++) {674 expect(properties[i].value).to.be.equal('Serotonin increase');675 expect(tokenData!.properties[i].value).to.be.equal('Serotonin increase');676 }677 expect(await targetToken.getProperties()).to.be.empty;678 });679680 itSub('Changes properties of a nested token according to permissions', async ({helper}) => {681 const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});682 const collectionB = await helper.nft.mintCollection(alice);683 const targetToken = await collectionA.mintToken(alice);684 const nestedToken = await collectionB.mintToken(alice, targetToken.nestingAccount());685686 await collectionB.addAdmin(alice, {Substrate: bob.address});687 await targetToken.transfer(alice, {Substrate: charlie.address});688689 const propertyKeys: string[] = [];690 let i = 0;691 for (const permission of permissions) {692 i++;693 if (!permission.permission.mutable) continue;694 695 let j = 0;696 for (const signer of permission.signers) {697 j++;698 const key = i + '_' + signer.address;699 propertyKeys.push(key);700701 await expect(702 nestedToken.collection.setTokenPropertyPermissions(alice, [{key: key, permission: permission.permission}]), 703 `on setting permission #${i} by alice`,704 ).to.be.fulfilled;705706 await expect(707 nestedToken.setProperties(signer, [{key, value: 'Serotonin increase'}]), 708 `on adding property #${i} by signer #${j}`,709 ).to.be.fulfilled;710711 await expect(712 nestedToken.setProperties(signer, [{key, value: 'Serotonin stable'}]), 713 `on changing property #${i} by signer #${j}`,714 ).to.be.fulfilled;715 }716 }717718 const properties = await nestedToken.getProperties(propertyKeys);719 const tokenData = await nestedToken.getData();720 for (let i = 0; i < properties.length; i++) {721 expect(properties[i].value).to.be.equal('Serotonin stable');722 expect(tokenData!.properties[i].value).to.be.equal('Serotonin stable');723 }724 expect(await targetToken.getProperties()).to.be.empty;725 });726727 itSub('Deletes properties of a nested token according to permissions', async ({helper}) => {728 const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});729 const collectionB = await helper.nft.mintCollection(alice);730 const targetToken = await collectionA.mintToken(alice);731 const nestedToken = await collectionB.mintToken(alice, targetToken.nestingAccount());732733 await collectionB.addAdmin(alice, {Substrate: bob.address});734 await targetToken.transfer(alice, {Substrate: charlie.address});735736 const propertyKeys: string[] = [];737 let i = 0;738 for (const permission of permissions) {739 i++;740 if (!permission.permission.mutable) continue;741 742 let j = 0;743 for (const signer of permission.signers) {744 j++;745 const key = i + '_' + signer.address;746 propertyKeys.push(key);747748 await expect(749 nestedToken.collection.setTokenPropertyPermissions(alice, [{key: key, permission: permission.permission}]), 750 `on setting permission #${i} by alice`,751 ).to.be.fulfilled;752753 await expect(754 nestedToken.setProperties(signer, [{key, value: 'Serotonin increase'}]), 755 `on adding property #${i} by signer #${j}`,756 ).to.be.fulfilled;757758 await expect(759 nestedToken.deleteProperties(signer, [key]), 760 `on deleting property #${i} by signer #${j}`,761 ).to.be.fulfilled;762 }763 }764765 expect(await nestedToken.getProperties(propertyKeys)).to.be.empty;766 expect((await nestedToken.getData())!.properties).to.be.empty;767 expect(await targetToken.getProperties()).to.be.empty;768 });769});770771describe('Negative Integration Test: Token Properties', () => {772 let alice: IKeyringPair; // collection owner773 let bob: IKeyringPair; // collection admin774 let charlie: IKeyringPair; // token owner775776 let constitution: {permission: any, signers: IKeyringPair[], sinner: IKeyringPair}[];777778 before(async () => {779 await usingPlaygrounds(async (helper, privateKey) => {780 const donor = privateKey('//Alice');781 let dave: IKeyringPair;782 [alice, bob, charlie, dave] = await helper.arrange.createAccounts([100n, 100n, 100n, 100n], donor);783784 // todo:playgrounds probably separate these tests later785 constitution = [786 {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},787 {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},788 {permission: {mutable: true, tokenOwner: true}, signers: [charlie], sinner: alice},789 {permission: {mutable: false, tokenOwner: true}, signers: [charlie], sinner: alice},790 {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},791 {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},792 ];793 });794 });795796 async function getConsumedSpace(api: any, collectionId: number, tokenId: number, mode: 'NFT' | 'RFT'): Promise<number> {797 return (await (mode == 'NFT' ? api.query.nonfungible : api.query.refungible).tokenProperties(collectionId, tokenId)).toJSON().consumedSpace;798 }799800 async function prepare(token: UniqueNFToken | UniqueRFToken, pieces: bigint): Promise<number> {801 await token.collection.addAdmin(alice, {Substrate: bob.address});802 await token.transfer(alice, {Substrate: charlie.address}, pieces);803804 let i = 0;805 for (const passage of constitution) {806 i++;807 const signer = passage.signers[0];808 809 await expect(810 token.collection.setTokenPropertyPermissions(alice, [{key: `${i}`, permission: passage.permission}]), 811 `on setting permission ${i} by alice`,812 ).to.be.fulfilled;813814 await expect(815 token.setProperties(signer, [{key: `${i}`, value: 'Serotonin increase'}]), 816 `on adding property ${i} by ${signer.address}`,817 ).to.be.fulfilled;818 }819820 const originalSpace = await getConsumedSpace(token.collection.helper.api, token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT'); 821 return originalSpace;822 }823824 async function testForbidsChangingDeletingPropertiesUserOutsideOfPermissions(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {825 const originalSpace = await prepare(token, pieces);826827 let i = 0;828 for (const forbiddance of constitution) {829 i++;830 if (!forbiddance.permission.mutable) continue;831832 await expect(833 token.setProperties(forbiddance.sinner, [{key: `${i}`, value: 'Serotonin down'}]), 834 `on failing to change property ${i} by the malefactor`,835 ).to.be.rejectedWith(/common\.NoPermission/);836837 await expect(838 token.deleteProperties(forbiddance.sinner, [`${i}`]), 839 `on failing to delete property ${i} by the malefactor`,840 ).to.be.rejectedWith(/common\.NoPermission/);841 }842843 const consumedSpace = await getConsumedSpace(token.collection.helper.api, token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT'); 844 expect(consumedSpace).to.be.equal(originalSpace);845 }846847 itSub('Forbids changing/deleting properties of a token if the user is outside of permissions (NFT)', async ({helper}) => {848 const collection = await helper.nft.mintCollection(alice);849 const token = await collection.mintToken(alice);850 await testForbidsChangingDeletingPropertiesUserOutsideOfPermissions(token, 1n);851 });852853 itSub.ifWithPallets('Forbids changing/deleting properties of a token if the user is outside of permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => {854 const collection = await helper.rft.mintCollection(alice);855 const token = await collection.mintToken(alice, 100n);856 await testForbidsChangingDeletingPropertiesUserOutsideOfPermissions(token, 100n);857 });858859 async function testForbidsChangingDeletingPropertiesIfPropertyImmutable(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {860 const originalSpace = await prepare(token, pieces);861862 let i = 0;863 for (const permission of constitution) {864 i++;865 if (permission.permission.mutable) continue;866867 await expect(868 token.setProperties(permission.signers[0], [{key: `${i}`, value: 'Serotonin down'}]), 869 `on failing to change property ${i} by signer #0`,870 ).to.be.rejectedWith(/common\.NoPermission/);871872 await expect(873 token.deleteProperties(permission.signers[0], [i.toString()]), 874 `on failing to delete property ${i} by signer #0`,875 ).to.be.rejectedWith(/common\.NoPermission/);876 }877 878 const consumedSpace = await getConsumedSpace(token.collection.helper.api, token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT'); 879 expect(consumedSpace).to.be.equal(originalSpace);880 }881882 itSub('Forbids changing/deleting properties of a token if the property is permanent (immutable) (NFT)', async ({helper}) => {883 const collection = await helper.nft.mintCollection(alice);884 const token = await collection.mintToken(alice);885 await testForbidsChangingDeletingPropertiesIfPropertyImmutable(token, 1n);886 });887888 itSub.ifWithPallets('Forbids changing/deleting properties of a token if the property is permanent (immutable) (ReFungible)', [Pallets.ReFungible], async ({helper}) => {889 const collection = await helper.rft.mintCollection(alice);890 const token = await collection.mintToken(alice, 100n);891 await testForbidsChangingDeletingPropertiesIfPropertyImmutable(token, 100n);892 });893894 async function testForbidsAddingPropertiesIfPropertyNotDeclared(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {895 const originalSpace = await prepare(token, pieces);896897 await expect(898 token.setProperties(alice, [{key: 'non-existent', value: 'I exist!'}]), 899 'on failing to add a previously non-existent property',900 ).to.be.rejectedWith(/common\.NoPermission/);901 902 await expect(903 token.collection.setTokenPropertyPermissions(alice, [{key: 'now-existent', permission: {}}]), 904 'on setting a new non-permitted property',905 ).to.be.fulfilled;906907 await expect(908 token.setProperties(alice, [{key: 'now-existent', value: 'I exist!'}]), 909 'on failing to add a property forbidden by the \'None\' permission',910 ).to.be.rejectedWith(/common\.NoPermission/);911912 expect(await token.getProperties(['non-existent', 'now-existent'])).to.be.empty;913 914 const consumedSpace = await getConsumedSpace(token.collection.helper.api, token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT'); 915 expect(consumedSpace).to.be.equal(originalSpace);916 }917918 itSub('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission (NFT)', async ({helper}) => {919 const collection = await helper.nft.mintCollection(alice);920 const token = await collection.mintToken(alice);921 await testForbidsAddingPropertiesIfPropertyNotDeclared(token, 1n);922 });923924 itSub.ifWithPallets('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission (ReFungible)', [Pallets.ReFungible], async ({helper}) => {925 const collection = await helper.rft.mintCollection(alice);926 const token = await collection.mintToken(alice, 100n);927 await testForbidsAddingPropertiesIfPropertyNotDeclared(token, 100n);928 });929930 async function testForbidsAddingTooManyProperties(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {931 const originalSpace = await prepare(token, pieces);932933 await expect(934 token.collection.setTokenPropertyPermissions(alice, [935 {key: 'a_holy_book', permission: {collectionAdmin: true, tokenOwner: true}}, 936 {key: 'young_years', permission: {collectionAdmin: true, tokenOwner: true}},937 ]), 938 'on setting new permissions for properties',939 ).to.be.fulfilled;940941 // Mute the general tx parsing error942 {943 console.error = () => {};944 await expect(token.setProperties(alice, [{key: 'a_holy_book', value: 'word '.repeat(6554)}]))945 .to.be.rejected;946 }947948 await expect(token.setProperties(alice, [949 {key: 'a_holy_book', value: 'word '.repeat(3277)}, 950 {key: 'young_years', value: 'neverending'.repeat(1490)},951 ])).to.be.rejectedWith(/common\.NoSpaceForProperty/);952 953 expect(await token.getProperties(['a_holy_book', 'young_years'])).to.be.empty;954 const consumedSpace = await getConsumedSpace(token.collection.helper.api, token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT'); 955 expect(consumedSpace).to.be.equal(originalSpace);956 }957958 itSub('Forbids adding too many properties to a token (NFT)', async ({helper}) => {959 const collection = await helper.nft.mintCollection(alice);960 const token = await collection.mintToken(alice);961 await testForbidsAddingTooManyProperties(token, 1n);962 });963964 itSub.ifWithPallets('Forbids adding too many properties to a token (ReFungible)', [Pallets.ReFungible], async ({helper}) => {965 const collection = await helper.rft.mintCollection(alice);966 const token = await collection.mintToken(alice, 100n);967 await testForbidsAddingTooManyProperties(token, 100n);968 });969});970971describe('ReFungible token properties permissions tests', () => {972 let alice: IKeyringPair;973 let bob: IKeyringPair;974 let charlie: IKeyringPair;975976 before(async function() {977 await usingPlaygrounds(async (helper, privateKey) => {978 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);979980 const donor = privateKey('//Alice');981 [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);982 });983 });984985 async function prepare(helper: UniqueHelper): Promise<UniqueRFToken> {986 const collection = await helper.rft.mintCollection(alice);987 const token = await collection.mintToken(alice, 100n);988 989 await collection.addAdmin(alice, {Substrate: bob.address});990 await collection.setTokenPropertyPermissions(alice, [{key: 'fractals', permission: {mutable: true, tokenOwner: true}}]);991 992 return token;993 }994995 itSub('Forbids adding token property with tokenOwner==true when signer doesn\'t have all pieces', async ({helper}) => {996 const token = await prepare(helper);997998 await token.transfer(alice, {Substrate: charlie.address}, 33n);9991000 await expect(token.setProperties(alice, [1001 {key: 'fractals', value: 'multiverse'}, 1002 ])).to.be.rejectedWith(/common\.NoPermission/);1003 });10041005 itSub('Forbids mutating token property with tokenOwher==true when signer doesn\'t have all pieces', async ({helper}) => {1006 const token = await prepare(helper);10071008 await expect(token.collection.setTokenPropertyPermissions(alice, [{key: 'fractals', permission: {mutable:true, tokenOwner: true}}]))1009 .to.be.fulfilled;10101011 await expect(token.setProperties(alice, [1012 {key: 'fractals', value: 'multiverse'}, 1013 ])).to.be.fulfilled;10141015 await token.transfer(alice, {Substrate: charlie.address}, 33n);10161017 await expect(token.setProperties(alice, [1018 {key: 'fractals', value: 'want to rule the world'}, 1019 ])).to.be.rejectedWith(/common\.NoPermission/);1020 });10211022 itSub('Forbids deleting token property with tokenOwner==true when signer doesn\'t have all pieces', async ({helper}) => {1023 const token = await prepare(helper);10241025 await expect(token.setProperties(alice, [1026 {key: 'fractals', value: 'one headline - why believe it'}, 1027 ])).to.be.fulfilled;10281029 await token.transfer(alice, {Substrate: charlie.address}, 33n);10301031 await expect(token.deleteProperties(alice, ['fractals'])).1032 to.be.rejectedWith(/common\.NoPermission/);1033 });10341035 itSub('Allows token property mutation with collectionOwner==true when admin doesn\'t have all pieces', async ({helper}) => {1036 const token = await prepare(helper);10371038 await token.transfer(alice, {Substrate: charlie.address}, 33n);10391040 await expect(token.collection.setTokenPropertyPermissions(alice, [{key: 'fractals', permission: {mutable:true, collectionAdmin: true}}]))1041 .to.be.fulfilled;10421043 await expect(token.setProperties(alice, [1044 {key: 'fractals', value: 'multiverse'}, 1045 ])).to.be.fulfilled;1046 });1047});tests/src/nesting/rules-smoke.test.tsdiffbeforeafterboth--- a/tests/src/nesting/rules-smoke.test.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import {expect} from 'chai';
-import {tokenIdToAddress} from '../eth/util/helpers';
-import usingApi, {executeTransaction} from '../substrate/substrate-api';
-import {createCollectionExpectSuccess, createFungibleItemExpectSuccess, createItemExpectSuccess, CrossAccountId, getCreateCollectionResult, requirePallets, Pallets} from '../util/helpers';
-import {IKeyringPair} from '@polkadot/types/types';
-
-describe('nesting check', () => {
- let alice!: IKeyringPair;
- let nestTarget!: CrossAccountId;
- before(async() => {
- await usingApi(async (api, privateKeyWrapper) => {
- alice = privateKeyWrapper('//Alice');
- const bob = privateKeyWrapper('//Bob');
- const events = await executeTransaction(api, alice, api.tx.unique.createCollectionEx({
- mode: 'NFT',
- permissions: {
- nesting: {tokenOwner: true, restricted: []},
- },
- }));
- const collection = getCreateCollectionResult(events).collectionId;
- const token = await createItemExpectSuccess(alice, collection, 'NFT', {Substrate: bob.address});
- nestTarget = {Ethereum: tokenIdToAddress(collection, token)};
- });
- });
-
- it('called for fungible', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'Fungible',decimalPoints:0}});
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(collection, nestTarget, {Fungible: {Value: 1}})))
- .to.be.rejectedWith(/^common\.UserIsNotAllowedToNest$/);
-
- await createFungibleItemExpectSuccess(alice, collection, {Value:1n}, {Substrate: alice.address});
- await expect(executeTransaction(api, alice, api.tx.unique.transfer(nestTarget, collection, 0, 1n)))
- .to.be.rejectedWith(/^common\.UserIsNotAllowedToNest$/);
- });
- });
-
- it('called for nonfungible', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(collection, nestTarget, {NFT: {properties: []}})))
- .to.be.rejectedWith(/^common\.UserIsNotAllowedToNest$/);
-
- const token = await createItemExpectSuccess(alice, collection, 'NFT', {Substrate: alice.address});
- await expect(executeTransaction(api, alice, api.tx.unique.transfer(nestTarget, collection, token, 1n)))
- .to.be.rejectedWith(/^common\.UserIsNotAllowedToNest$/);
- });
- });
-
- it('called for refungible', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
-
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(collection, nestTarget, {ReFungible: {}})))
- .to.be.rejectedWith(/^common\.UserIsNotAllowedToNest$/);
-
- const token = await createItemExpectSuccess(alice, collection, 'ReFungible', {Substrate: alice.address});
- await expect(executeTransaction(api, alice, api.tx.unique.transfer(nestTarget, collection, token, 1n)))
- .to.be.rejectedWith(/^common\.UserIsNotAllowedToNest$/);
- });
- });
-});
tests/src/nesting/unnest.test.tsdiffbeforeafterboth--- a/tests/src/nesting/unnest.test.ts
+++ b/tests/src/nesting/unnest.test.ts
@@ -1,162 +1,126 @@
-import {expect} from 'chai';
-import {tokenIdToAddress} from '../eth/util/helpers';
-import usingApi, {executeTransaction} from '../substrate/substrate-api';
-import {
- createCollectionExpectSuccess,
- createItemExpectSuccess,
- getBalance,
- getTokenOwner,
- normalizeAccountId,
- setCollectionPermissionsExpectSuccess,
- transferExpectSuccess,
- transferFromExpectSuccess,
- requirePallets,
- Pallets,
-} from '../util/helpers';
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
import {IKeyringPair} from '@polkadot/types/types';
+import {expect, itSub, Pallets, usingPlaygrounds} from '../util/playgrounds';
-let alice: IKeyringPair;
-let bob: IKeyringPair;
+describe('Integration Test: Unnesting', () => {
+ let alice: IKeyringPair;
-describe('Integration Test: Unnesting', () => {
before(async () => {
- await usingApi(async (api, privateKeyWrapper) => {
- alice = privateKeyWrapper('//Alice');
- bob = privateKeyWrapper('//Bob');
+ await usingPlaygrounds(async (helper, privateKey) => {
+ const donor = privateKey('//Alice');
+ [alice] = await helper.arrange.createAccounts([50n], donor);
});
});
- it('NFT: allows the owner to successfully unnest a token', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken)};
-
- // Create a nested token
- const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', targetAddress);
+ itSub('NFT: allows the owner to successfully unnest a token', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+ const targetToken = await collection.mintToken(alice);
+
+ // Create a nested token
+ const nestedToken = await collection.mintToken(alice, targetToken.nestingAccount());
- // Unnest
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.transferFrom(normalizeAccountId(targetAddress), normalizeAccountId(alice), collection, nestedToken, 1),
- ), 'while unnesting').to.not.be.rejected;
- expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: alice.address});
+ // Unnest
+ await expect(nestedToken.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}), 'while unnesting').to.be.fulfilled;
+ expect(await nestedToken.getOwner()).to.be.deep.equal({Substrate: alice.address});
- // Nest and burn
- await transferExpectSuccess(collection, nestedToken, alice, targetAddress);
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.burnFrom(collection, normalizeAccountId(targetAddress), nestedToken, 1),
- ), 'while burning').to.not.be.rejected;
- await expect(getTokenOwner(api, collection, nestedToken)).to.be.rejected;
- });
+ // Nest and burn
+ await nestedToken.nest(alice, targetToken);
+ await expect(nestedToken.burnFrom(alice, targetToken.nestingAccount()), 'while burning').to.be.fulfilled;
+ await expect(nestedToken.getOwner()).to.be.rejected;
});
- it('Fungible: allows the owner to successfully unnest a token', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken)};
+ itSub('Fungible: allows the owner to successfully unnest a token', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+ const targetToken = await collection.mintToken(alice);
- const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
- const nestedToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');
+ const collectionFT = await helper.ft.mintCollection(alice);
+
+ // Nest and unnest
+ await collectionFT.mint(alice, 10n, targetToken.nestingAccount());
+ await expect(collectionFT.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 9n), 'while unnesting').to.be.fulfilled;
+ expect(await collectionFT.getBalance({Substrate: alice.address})).to.be.equal(9n);
+ expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(1n);
- // Nest and unnest
- await transferExpectSuccess(collectionFT, nestedToken, alice, targetAddress, 1, 'Fungible');
- await transferFromExpectSuccess(collectionFT, nestedToken, alice, targetAddress, alice, 1, 'Fungible');
-
- // Nest and burn
- await transferExpectSuccess(collectionFT, nestedToken, alice, targetAddress, 1, 'Fungible');
- const balanceBefore = await getBalance(api, collectionFT, normalizeAccountId(targetAddress), nestedToken);
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.burnFrom(collectionFT, normalizeAccountId(targetAddress), nestedToken, 1),
- ), 'while burning').to.not.be.rejected;
- const balanceAfter = await getBalance(api, collectionFT, normalizeAccountId(targetAddress), nestedToken);
- expect(balanceAfter + BigInt(1)).to.be.equal(balanceBefore);
- });
+ // Nest and burn
+ await collectionFT.transfer(alice, targetToken.nestingAccount(), 5n);
+ await expect(collectionFT.burnTokensFrom(alice, targetToken.nestingAccount(), 6n), 'while burning').to.be.fulfilled;
+ expect(await collectionFT.getBalance({Substrate: alice.address})).to.be.equal(4n);
+ expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(0n);
+ expect(await targetToken.getChildren()).to.be.length(0);
});
- it('ReFungible: allows the owner to successfully unnest a token', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
+ itSub.ifWithPallets('ReFungible: allows the owner to successfully unnest a token', [Pallets.ReFungible], async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+ const targetToken = await collection.mintToken(alice);
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken)};
-
- const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
- const nestedToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');
-
- // Nest and unnest
- await transferExpectSuccess(collectionRFT, nestedToken, alice, targetAddress, 1, 'ReFungible');
- await transferFromExpectSuccess(collectionRFT, nestedToken, alice, targetAddress, alice, 1, 'ReFungible');
+ const collectionRFT = await helper.rft.mintCollection(alice);
+
+ // Nest and unnest
+ const token = await collectionRFT.mintToken(alice, 10n, targetToken.nestingAccount());
+ await expect(token.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 9n), 'while unnesting').to.be.fulfilled;
+ expect(await token.getBalance({Substrate: alice.address})).to.be.equal(9n);
+ expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(1n);
- // Nest and burn
- await transferExpectSuccess(collectionRFT, nestedToken, alice, targetAddress, 1, 'ReFungible');
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.burnFrom(collectionRFT, normalizeAccountId(targetAddress), nestedToken, 1),
- ), 'while burning').to.not.be.rejected;
- const balance = await getBalance(api, collectionRFT, normalizeAccountId(targetAddress), nestedToken);
- expect(balance).to.be.equal(0n);
- });
+ // Nest and burn
+ await token.transfer(alice, targetToken.nestingAccount(), 5n);
+ await expect(token.burnFrom(alice, targetToken.nestingAccount(), 6n), 'while burning').to.be.fulfilled;
+ expect(await token.getBalance({Substrate: alice.address})).to.be.equal(4n);
+ expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(0n);
+ expect(await targetToken.getChildren()).to.be.length(0);
});
});
describe('Negative Test: Unnesting', () => {
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
+
before(async () => {
- await usingApi(async (api, privateKeyWrapper) => {
- alice = privateKeyWrapper('//Alice');
- bob = privateKeyWrapper('//Bob');
+ await usingPlaygrounds(async (helper, privateKey) => {
+ const donor = privateKey('//Alice');
+ [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor);
});
});
- it('Disallows a non-owner to unnest/burn a token', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken)};
+ itSub('Disallows a non-owner to unnest/burn a token', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+ const targetToken = await collection.mintToken(alice);
- // Create a nested token
- const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', targetAddress);
+ // Create a nested token
+ const nestedToken = await collection.mintToken(alice, targetToken.nestingAccount());
- // Try to unnest
- await expect(executeTransaction(
- api,
- bob,
- api.tx.unique.transferFrom(normalizeAccountId(targetAddress), normalizeAccountId(bob), collection, nestedToken, 1),
- ), 'while unnesting').to.be.rejectedWith(/^common\.ApprovedValueTooLow$/);
- expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});
+ // Try to unnest
+ await expect(nestedToken.unnest(bob, targetToken, {Substrate: alice.address})).to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+ expect(await nestedToken.getOwner()).to.be.deep.equal(targetToken.nestingAccount().toLowerCase());
- // Try to burn
- await expect(executeTransaction(
- api,
- bob,
- api.tx.unique.burnFrom(collection, normalizeAccountId(bob.address), nestedToken, 1),
- ), 'while burning').to.not.be.rejectedWith(/^common\.ApprovedValueTooLow$/);
- expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});
- });
+ // Try to burn
+ await expect(nestedToken.burnFrom(bob, targetToken.nestingAccount())).to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+ expect(await nestedToken.getOwner()).to.be.deep.equal(targetToken.nestingAccount().toLowerCase());
});
// todo another test for creating excessive depth matryoshka with Ethereum?
// Recursive nesting
- it('Prevents Ouroboros creation', async () => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
+ itSub('Prevents Ouroboros creation', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+ const targetToken = await collection.mintToken(alice);
- // Create a nested token ouroboros
- const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});
- await expect(transferExpectSuccess(collection, targetToken, alice, {Ethereum: tokenIdToAddress(collection, nestedToken)})).to.be.rejectedWith(/^structure\.OuroborosDetected$/);
+ // Fail to create a nested token ouroboros
+ const nestedToken = await collection.mintToken(alice, targetToken.nestingAccount());
+ await expect(targetToken.nest(alice, nestedToken)).to.be.rejectedWith(/^structure\.OuroborosDetected$/);
});
});
tests/src/refungible.test.tsdiffbeforeafterboth--- a/tests/src/refungible.test.ts
+++ b/tests/src/refungible.test.ts
@@ -124,7 +124,7 @@
const token = await collection.mintToken(alice, 100n);
expect(await collection.isTokenExists(token.tokenId)).to.be.true;
expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n);
- expect((await token.burn(alice, 99n)).success).to.be.true;
+ expect(await token.burn(alice, 99n)).to.be.true;
expect(await collection.isTokenExists(token.tokenId)).to.be.true;
expect(await token.getBalance({Substrate: alice.address})).to.be.equal(1n);
});
@@ -136,7 +136,7 @@
expect(await collection.isTokenExists(token.tokenId)).to.be.true;
expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n);
- expect((await token.burn(alice, 100n)).success).to.be.true;
+ expect(await token.burn(alice, 100n)).to.be.true;
expect(await collection.isTokenExists(token.tokenId)).to.be.false;
});
@@ -152,17 +152,17 @@
expect(await token.getBalance({Substrate: alice.address})).to.be.equal(40n);
expect(await token.getBalance({Substrate: bob.address})).to.be.equal(60n);
- expect((await token.burn(alice, 40n)).success).to.be.true;
+ expect(await token.burn(alice, 40n)).to.be.true;
expect(await collection.isTokenExists(token.tokenId)).to.be.true;
expect(await token.getBalance({Substrate: alice.address})).to.be.equal(0n);
- expect((await token.burn(bob, 59n)).success).to.be.true;
+ expect(await token.burn(bob, 59n)).to.be.true;
expect(await token.getBalance({Substrate: bob.address})).to.be.equal(1n);
expect(await collection.isTokenExists(token.tokenId)).to.be.true;
- expect((await token.burn(bob, 1n)).success).to.be.true;
+ expect(await token.burn(bob, 1n)).to.be.true;
expect(await collection.isTokenExists(token.tokenId)).to.be.false;
});
tests/src/rpc.test.tsdiffbeforeafterboth--- a/tests/src/rpc.test.ts
+++ b/tests/src/rpc.test.ts
@@ -16,7 +16,7 @@
import {IKeyringPair} from '@polkadot/types/types';
import {usingPlaygrounds, itSub, expect} from './util/playgrounds';
-import {crossAccountIdFromLower} from './util/playgrounds/unique';
+import {CrossAccountId} from './util/playgrounds/unique';
describe('integration test: RPC methods', () => {
let donor: IKeyringPair;
@@ -55,7 +55,7 @@
// Set-up over
const owners = await helper.callRpc('api.rpc.unique.tokenOwners', [collection.collectionId, 0]);
- const ids = (owners.toJSON() as any[]).map(crossAccountIdFromLower);
+ const ids = (owners.toJSON() as any[]).map(CrossAccountId.fromLowerCaseKeys);
expect(ids).to.deep.include.members([{Substrate: alice.address}, ethAcc, {Substrate: bob.address}, ...facelessCrowd]);
expect(owners.length == 10).to.be.true;
tests/src/util/playgrounds/index.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/index.ts
+++ b/tests/src/util/playgrounds/index.ts
@@ -9,7 +9,6 @@
import '../../interfaces/augment-api-events';
import {DevUniqueHelper, SilentLogger, SilentConsole} from './unique.dev';
-
chai.use(chaiAsPromised);
export const expect = chai.expect;
@@ -63,6 +62,12 @@
});
});
}
+export async function itSubIfWithPallet(name: string, required: string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) {
+ return itSub(name, cb, {requiredPallets: required, ...opts});
+}
itSub.only = (name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any) => itSub(name, cb, {only: true});
itSub.skip = (name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any) => itSub(name, cb, {skip: true});
-itSub.ifWithPallets = (name: string, required: string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any) => itSub(name, cb, {requiredPallets: required});
+
+itSubIfWithPallet.only = (name: string, required: string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any) => itSubIfWithPallet(name, required, cb, {only: true});
+itSubIfWithPallet.skip = (name: string, required: string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any) => itSubIfWithPallet(name, required, cb, {skip: true});
+itSub.ifWithPallets = itSubIfWithPallet;
tests/src/util/playgrounds/types.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/types.ts
+++ b/tests/src/util/playgrounds/types.ts
@@ -94,15 +94,15 @@
export interface IProperty {
key: string;
- value: string;
+ value?: string;
}
export interface ITokenPropertyPermission {
key: string;
permission: {
- mutable: boolean;
- tokenOwner: boolean;
- collectionAdmin: boolean;
+ mutable?: boolean;
+ tokenOwner?: boolean;
+ collectionAdmin?: boolean;
}
}
tests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/unique.dev.ts
+++ b/tests/src/util/playgrounds/unique.dev.ts
@@ -6,6 +6,7 @@
import {ApiPromise, WsProvider} from '@polkadot/api';
import * as defs from '../../interfaces/definitions';
import {IKeyringPair} from '@polkadot/types/types';
+import {ICrossAccountId} from './types';
export class SilentLogger {
@@ -230,6 +231,17 @@
const block2date = await findCreationDate(block2);
if(block2date! - block1date! < 9000) return true;
};
+
+ async calculcateFee(payer: ICrossAccountId, promise: () => Promise<any>): Promise<bigint> {
+ const address = payer.Substrate ? payer.Substrate : await this.helper.address.ethToSubstrate(payer.Ethereum!);
+ let balance = await this.helper.balance.getSubstrate(address);
+
+ await promise();
+
+ balance -= await this.helper.balance.getSubstrate(address);
+
+ return balance;
+ }
}
class WaitGroup {
tests/src/util/playgrounds/unique.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/unique.ts
+++ b/tests/src/util/playgrounds/unique.ts
@@ -11,12 +11,42 @@
import {IKeyringPair} from '@polkadot/types/types';
import {IApiListeners, IBlock, IEvent, IChainProperties, ICollectionCreationOptions, ICollectionLimits, ICollectionPermissions, ICrossAccountId, ICrossAccountIdLower, ILogger, INestingPermissions, IProperty, IStakingInfo, ISubstrateBalance, IToken, ITokenPropertyPermission, ITransactionResult, IUniqueHelperLog, TApiAllowedListeners, TEthereumAccount, TSigner, TSubstrateAccount, TUniqueNetworks} from './types';
-export const crossAccountIdFromLower = (lowerAddress: ICrossAccountIdLower): ICrossAccountId => {
- const address = {} as ICrossAccountId;
- if(lowerAddress.substrate) address.Substrate = lowerAddress.substrate;
- if(lowerAddress.ethereum) address.Ethereum = lowerAddress.ethereum;
- return address;
-};
+export class CrossAccountId implements ICrossAccountId {
+ Substrate?: TSubstrateAccount;
+ Ethereum?: TEthereumAccount;
+
+ constructor(account: ICrossAccountId) {
+ if (account.Substrate) this.Substrate = account.Substrate;
+ if (account.Ethereum) this.Ethereum = account.Ethereum;
+ }
+
+ static fromKeyring(account: IKeyringPair) {
+ return new CrossAccountId({Substrate: account.address});
+ }
+
+ static fromLowerCaseKeys(address: ICrossAccountIdLower): CrossAccountId {
+ return new CrossAccountId({Substrate: address.substrate, Ethereum: address.ethereum});
+ }
+
+ static normalizeSubstrateAddress(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {
+ return encodeAddress(decodeAddress(address), ss58Format);
+ }
+
+ static withNormalizedSubstrate(address: TSubstrateAccount, ss58Format = 42): CrossAccountId {
+ return new CrossAccountId({Substrate: CrossAccountId.normalizeSubstrateAddress(address, ss58Format)});
+ }
+
+ withNormalizedSubstrate(ss58Format = 42): CrossAccountId {
+ if (this.Substrate) return CrossAccountId.withNormalizedSubstrate(this.Substrate, ss58Format);
+ return this;
+ }
+
+ toLowerCase(): CrossAccountId {
+ if (this.Substrate) this.Substrate = this.Substrate.toLowerCase();
+ if (this.Ethereum) this.Ethereum = this.Ethereum.toLowerCase();
+ return this;
+ }
+}
const nesting = {
toChecksumAddress(address: string): string {
@@ -55,8 +85,12 @@
RPC: 'rpc',
};
- static getNestingTokenAddress(collectionId: number, tokenId: number) {
- return nesting.tokenIdToAddress(collectionId, tokenId);
+ static getTokenAccount(token: IToken): CrossAccountId {
+ return new CrossAccountId({Ethereum: this.getTokenAddress(token)});
+ }
+
+ static getTokenAddress(token: IToken): string {
+ return nesting.tokenIdToAddress(token.collectionId, token.tokenId);
}
static getDefaultLogger(): ILogger {
@@ -86,11 +120,7 @@
return keyring.addFromUri(seed);
}
- static normalizeSubstrateAddress(address: string, ss58Format = 42) {
- return encodeAddress(decodeAddress(address), ss58Format);
- }
-
- static extractCollectionIdFromCreationResult(creationResult: ITransactionResult) {
+ static extractCollectionIdFromCreationResult(creationResult: ITransactionResult): number {
if (creationResult.status !== this.transactionStatus.SUCCESS) {
throw Error('Unable to create collection!');
}
@@ -109,12 +139,15 @@
return collectionId;
}
- static extractTokensFromCreationResult(creationResult: ITransactionResult) {
+ static extractTokensFromCreationResult(creationResult: ITransactionResult): {
+ success: boolean,
+ tokens: {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[],
+ } {
if (creationResult.status !== this.transactionStatus.SUCCESS) {
throw Error('Unable to create tokens!');
}
let success = false;
- const tokens = [] as any;
+ const tokens = [] as {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[];
creationResult.result.events.forEach(({event: {data, method, section}}) => {
if (method === 'ExtrinsicSuccess') {
success = true;
@@ -122,19 +155,23 @@
tokens.push({
collectionId: parseInt(data[0].toString(), 10),
tokenId: parseInt(data[1].toString(), 10),
- owner: data[2].toJSON(),
+ owner: data[2].toHuman(),
+ amount: data[3].toBigInt(),
});
}
});
return {success, tokens};
}
- static extractTokensFromBurnResult(burnResult: ITransactionResult) {
+ static extractTokensFromBurnResult(burnResult: ITransactionResult): {
+ success: boolean,
+ tokens: {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[],
+ } {
if (burnResult.status !== this.transactionStatus.SUCCESS) {
throw Error('Unable to burn tokens!');
}
let success = false;
- const tokens = [] as any;
+ const tokens = [] as {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[];
burnResult.result.events.forEach(({event: {data, method, section}}) => {
if (method === 'ExtrinsicSuccess') {
success = true;
@@ -142,14 +179,15 @@
tokens.push({
collectionId: parseInt(data[0].toString(), 10),
tokenId: parseInt(data[1].toString(), 10),
- owner: data[2].toJSON(),
+ owner: data[2].toHuman(),
+ amount: data[3].toBigInt(),
});
}
});
return {success, tokens};
}
- static findCollectionInEvents(events: {event: IEvent}[], collectionId: number, expectedSection: string, expectedMethod: string) {
+ static findCollectionInEvents(events: {event: IEvent}[], collectionId: number, expectedSection: string, expectedMethod: string): boolean {
let eventId = null;
events.forEach(({event: {data, method, section}}) => {
if ((section === expectedSection) && (method === expectedMethod)) {
@@ -170,8 +208,8 @@
Object.keys(address).forEach(k => {
obj[k.toLocaleLowerCase()] = address[k as 'Substrate' | 'Ethereum'];
});
- if(obj.substrate) return {Substrate: this.normalizeSubstrateAddress(obj.substrate)};
- if(obj.ethereum) return {Ethereum: obj.ethereum.toLocaleLowerCase()};
+ if(obj.substrate) return CrossAccountId.withNormalizedSubstrate(obj.substrate);
+ if(obj.ethereum) return CrossAccountId.fromLowerCaseKeys(obj).toLowerCase();
return address;
};
let transfer = {collectionId: null, tokenId: null, from: null, to: null, amount: 1} as any;
@@ -555,7 +593,7 @@
name: string;
description: string;
tokensCount: number;
- admins: ICrossAccountId[];
+ admins: CrossAccountId[];
normalizedOwner: TSubstrateAccount;
raw: any
} | null> {
@@ -588,15 +626,11 @@
* @example await getAdmins(1)
* @returns array of administrators
*/
- async getAdmins(collectionId: number, normalize = false): Promise<ICrossAccountId[]> {
+ async getAdmins(collectionId: number, normalize = false): Promise<CrossAccountId[]> {
const admins = (await this.helper.callRpc('api.rpc.unique.adminlist', [collectionId])).toHuman();
return normalize
- ? admins.map((address: any) => {
- return address.Substrate
- ? {Substrate: this.helper.address.normalizeSubstrate(address.Substrate)}
- : address;
- })
+ ? admins.map((address: CrossAccountId) => address.withNormalizedSubstrate())
: admins;
}
@@ -607,14 +641,10 @@
* @example await getAllowList(1)
* @returns array of allow-listed addresses
*/
- async getAllowList(collectionId: number, normalize = false): Promise<ICrossAccountId[]> {
+ async getAllowList(collectionId: number, normalize = false): Promise<CrossAccountId[]> {
const allowListed = (await this.helper.callRpc('api.rpc.unique.allowlist', [collectionId])).toHuman();
return normalize
- ? allowListed.map((address: any) => {
- return address.Substrate
- ? {Substrate: this.helper.address.normalizeSubstrate(address.Substrate)}
- : address;
- })
+ ? allowListed.map((address: CrossAccountId) => address.withNormalizedSubstrate())
: allowListed;
}
@@ -897,6 +927,18 @@
}
/**
+ * Get collection properties.
+ *
+ * @param collectionId ID of collection
+ * @param propertyKeys optionally filter the returned properties to only these keys
+ * @example getProperties(1219, ['location', 'date', 'time', 'isParadise']);
+ * @returns array of key-value pairs
+ */
+ async getProperties(collectionId: number, propertyKeys: string[] | null = null): Promise<IProperty[]> {
+ return (await this.helper.callRpc('api.rpc.unique.collectionProperties', [collectionId, ...(propertyKeys === null ? [] : [propertyKeys])])).toHuman();
+ }
+
+ /**
* Deletes onchain properties from the collection.
*
* @param signer keyring of signer
@@ -967,12 +1009,9 @@
* @param tokenId ID of token
* @param amount amount of tokens to be burned. For NFT must be set to 1n
* @example burnToken(aliceKeyring, 10, 5);
- * @returns ```true``` and burnt token number is extrinsic success. Otherwise ```false``` and ```null```
+ * @returns ```true``` if the extrinsic is successful, otherwise ```false```
*/
- async burnToken(signer: TSigner, collectionId: number, tokenId: number, amount=1n): Promise<{
- success: boolean,
- token: number | null
- }> {
+ async burnToken(signer: TSigner, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {
const burnResult = await this.helper.executeExtrinsic(
signer,
'api.tx.unique.burnItem', [collectionId, tokenId, amount],
@@ -980,7 +1019,7 @@
);
const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);
if (burnedTokens.tokens.length > 1) throw Error('Burned multiple tokens');
- return {success: burnedTokens.success, token: burnedTokens.tokens.length > 0 ? burnedTokens.tokens[0] : null};
+ return burnedTokens.success;
}
/**
@@ -988,13 +1027,13 @@
*
* @param signer keyring of signer
* @param collectionId ID of collection
+ * @param tokenId ID of token
* @param fromAddressObj address on behalf of which the token will be burnt
- * @param tokenId ID of token
* @param amount amount of tokens to be burned. For NFT must be set to 1n
* @example burnTokenFrom(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."}, 5, {Ethereum: "0x9F0583DbB85..."})
* @returns ```true``` if extrinsic success, otherwise ```false```
*/
- async burnTokenFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, tokenId: number, amount=1n): Promise<boolean> {
+ async burnTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {
const burnResult = await this.helper.executeExtrinsic(
signer,
'api.tx.unique.burnFrom', [collectionId, fromAddressObj, tokenId, amount],
@@ -1087,8 +1126,8 @@
*/
async getToken(collectionId: number, tokenId: number, propertyKeys: string[] = [], blockHashAt?: string): Promise<{
properties: IProperty[];
- owner: ICrossAccountId;
- normalizedOwner: ICrossAccountId;
+ owner: CrossAccountId;
+ normalizedOwner: CrossAccountId;
}| null> {
let tokenData;
if(typeof blockHashAt === 'undefined') {
@@ -1106,9 +1145,11 @@
if (tokenData === null || tokenData.owner === null) return null;
const owner = {} as any;
for (const key of Object.keys(tokenData.owner)) {
- owner[key.toLocaleLowerCase()] = key.toLocaleLowerCase() === 'substrate' ? this.helper.address.normalizeSubstrate(tokenData.owner[key]) : tokenData.owner[key];
+ owner[key.toLocaleLowerCase()] = key.toLocaleLowerCase() == 'substrate'
+ ? CrossAccountId.normalizeSubstrateAddress(tokenData.owner[key])
+ : tokenData.owner[key];
}
- tokenData.normalizedOwner = crossAccountIdFromLower(owner);
+ tokenData.normalizedOwner = CrossAccountId.fromLowerCaseKeys(owner);
return tokenData;
}
@@ -1117,7 +1158,7 @@
*
* @param signer keyring of signer
* @param collectionId ID of collection
- * @param permissions permissions to change a property by the collection owner or admin
+ * @param permissions permissions to change a property by the collection admin or token owner
* @example setTokenPropertyPermissions(
* aliceKeyring, 10, [{key: "gender", permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}]
* )
@@ -1134,6 +1175,18 @@
}
/**
+ * Get token property permissions.
+ *
+ * @param collectionId ID of collection
+ * @param propertyKeys optionally filter the returned property permissions to only these keys
+ * @example getPropertyPermissions(1219, ['location', 'date', 'time', 'isParadise']);
+ * @returns array of key-permission pairs
+ */
+ async getPropertyPermissions(collectionId: number, propertyKeys: string[] | null = null): Promise<ITokenPropertyPermission[]> {
+ return (await this.helper.callRpc('api.rpc.unique.propertyPermissions', [collectionId, ...(propertyKeys === null ? [] : [propertyKeys])])).toHuman();
+ }
+
+ /**
* Set token properties
*
* @param signer keyring of signer
@@ -1154,6 +1207,19 @@
}
/**
+ * Get properties, metadata assigned to a token.
+ *
+ * @param collectionId ID of collection
+ * @param tokenId ID of token
+ * @param propertyKeys optionally filter the returned properties to only these keys
+ * @example getTokenProperties(1219, ['location', 'date', 'time', 'isParadise']);
+ * @returns array of key-value pairs
+ */
+ async getTokenProperties(collectionId: number, tokenId: number, propertyKeys: string[] | null = null): Promise<IProperty[]> {
+ return (await this.helper.callRpc('api.rpc.unique.tokenProperties', [collectionId, tokenId, ...(propertyKeys === null ? [] : [propertyKeys])])).toHuman();
+ }
+
+ /**
* Delete the provided properties of a token
* @param signer keyring of signer
* @param collectionId ID of collection
@@ -1181,7 +1247,7 @@
* @example mintCollection(aliceKeyring, {name: 'New', description: "New collection", tokenPrefix: "NEW"}, "NFT")
* @returns object of the created collection
*/
- async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT'): Promise<UniqueCollectionBase> {
+ async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT'): Promise<UniqueBaseCollection> {
collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object
collectionOptions.mode = (mode === 'NFT') ? {nft: null} : {refungible: null};
for (const key of ['name', 'description', 'tokenPrefix']) {
@@ -1223,8 +1289,8 @@
* @example getTokenObject(10, 5);
* @returns instance of UniqueNFTToken
*/
- getTokenObject(collectionId: number, tokenId: number): UniqueNFTToken {
- return new UniqueNFTToken(tokenId, this.getCollectionObject(collectionId));
+ getTokenObject(collectionId: number, tokenId: number): UniqueNFToken {
+ return new UniqueNFToken(tokenId, this.getCollectionObject(collectionId));
}
/**
@@ -1235,14 +1301,14 @@
* @example getTokenOwner(10, 5);
* @returns Address in CrossAccountId format, e.g. {Substrate: "5DnSF6RRjwteE3BrCj..."}
*/
- async getTokenOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<ICrossAccountId> {
+ async getTokenOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId> {
let owner;
if (typeof blockHashAt === 'undefined') {
owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId]);
} else {
owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId, blockHashAt]);
}
- return crossAccountIdFromLower(owner.toJSON());
+ return CrossAccountId.fromLowerCaseKeys(owner.toJSON());
}
/**
@@ -1294,7 +1360,7 @@
* @example getTokenTopmostOwner(10, 5);
* @returns address in CrossAccountId format, e.g. {Substrate: "5DyN4Y92vZCjv38fg..."}
*/
- async getTokenTopmostOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<ICrossAccountId | null> {
+ async getTokenTopmostOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId | null> {
let owner;
if (typeof blockHashAt === 'undefined') {
owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId]);
@@ -1304,9 +1370,7 @@
if (owner === null) return null;
- owner = owner.toHuman();
-
- return owner.Substrate ? {Substrate: this.helper.address.normalizeSubstrate(owner.Substrate)} : owner;
+ return owner.toHuman();
}
/**
@@ -1339,7 +1403,7 @@
* @returns ```true``` if extrinsic success, otherwise ```false```
*/
async nestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken): Promise<boolean> {
- const rootTokenAddress = {Ethereum: this.helper.util.getNestingTokenAddress(rootTokenObj.collectionId, rootTokenObj.tokenId)};
+ const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);
const result = await this.transferToken(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress);
if(!result) {
throw Error('Unable to nest token!');
@@ -1357,7 +1421,7 @@
* @returns ```true``` if extrinsic success, otherwise ```false```
*/
async unnestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken, toAddressObj: ICrossAccountId): Promise<boolean> {
- const rootTokenAddress = {Ethereum: this.helper.util.getNestingTokenAddress(rootTokenObj.collectionId, rootTokenObj.tokenId)};
+ const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);
const result = await this.transferTokenFrom(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress, toAddressObj);
if(!result) {
throw Error('Unable to unnest token!');
@@ -1377,7 +1441,7 @@
* })
* @returns object of the created collection
*/
- async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions): Promise<UniqueNFTCollection> {
+ async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueNFTCollection> {
return await super.mintCollection(signer, collectionOptions, 'NFT') as UniqueNFTCollection;
}
@@ -1387,7 +1451,7 @@
* @param data token data
* @returns created token object
*/
- async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; properties?: IProperty[]; }): Promise<UniqueNFTToken> {
+ async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; properties?: IProperty[]; }): Promise<UniqueNFToken> {
const creationResult = await this.helper.executeExtrinsic(
signer,
'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {
@@ -1418,7 +1482,7 @@
* }]);
* @returns ```true``` if extrinsic success, otherwise ```false```
*/
- async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]): Promise<UniqueNFTToken[]> {
+ async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]): Promise<UniqueNFToken[]> {
const creationResult = await this.helper.executeExtrinsic(
signer,
'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}],
@@ -1446,7 +1510,7 @@
* }]);
* @returns array of newly created tokens
*/
- async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {properties?: IProperty[]}[]): Promise<UniqueNFTToken[]> {
+ async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {properties?: IProperty[]}[]): Promise<UniqueNFToken[]> {
const rawTokens = [];
for (const token of tokens) {
const raw = {NFT: {properties: token.properties}};
@@ -1495,8 +1559,8 @@
* @example getTokenObject(10, 5);
* @returns instance of UniqueNFTToken
*/
- getTokenObject(collectionId: number, tokenId: number): UniqueRFTToken {
- return new UniqueRFTToken(tokenId, this.getCollectionObject(collectionId));
+ getTokenObject(collectionId: number, tokenId: number): UniqueRFToken {
+ return new UniqueRFToken(tokenId, this.getCollectionObject(collectionId));
}
/**
@@ -1506,8 +1570,8 @@
* @example getTokenTop10Owners(10, 5);
* @returns array of top 10 owners
*/
- async getTokenTop10Owners(collectionId: number, tokenId: number): Promise<ICrossAccountId[]> {
- return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, tokenId])).toJSON().map(crossAccountIdFromLower);
+ async getTokenTop10Owners(collectionId: number, tokenId: number): Promise<CrossAccountId[]> {
+ return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, tokenId])).toJSON().map(CrossAccountId.fromLowerCaseKeys);
}
/**
@@ -1563,7 +1627,7 @@
* })
* @returns object of the created collection
*/
- async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions): Promise<UniqueRFTCollection> {
+ async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueRFTCollection> {
return await super.mintCollection(signer, collectionOptions, 'RFT') as UniqueRFTCollection;
}
@@ -1574,7 +1638,7 @@
* @example mintToken(aliceKeyring, {collectionId: 10, owner: {Substrate: '5GHoZe9c73RYbVzq...'}, pieces: 10000n});
* @returns created token object
*/
- async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; pieces: bigint; properties?: IProperty[]; }): Promise<UniqueRFTToken> {
+ async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; pieces: bigint; properties?: IProperty[]; }): Promise<UniqueRFToken> {
const creationResult = await this.helper.executeExtrinsic(
signer,
'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {
@@ -1591,7 +1655,7 @@
return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);
}
- async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFTToken[]> {
+ async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFToken[]> {
throw Error('Not implemented');
const creationResult = await this.helper.executeExtrinsic(
signer,
@@ -1611,7 +1675,7 @@
* @example mintMultipleTokensWithOneOwner(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, [{pieces: 100000n, properties: [{key: "gender", value: "male"}]}]);
* @returns array of newly created RFT tokens
*/
- async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFTToken[]> {
+ async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFToken[]> {
const rawTokens = [];
for (const token of tokens) {
const raw = {ReFungible: {pieces: token.pieces, properties: token.properties}};
@@ -1633,13 +1697,27 @@
* @param tokenId ID of token
* @param amount number of pieces to be burnt
* @example burnToken(aliceKeyring, 10, 5);
- * @returns ```true``` and burnt token number is extrinsic success. Otherwise ```false``` and ```null```
+ * @returns ```true``` if the extrinsic is successful, otherwise ```false```
*/
- async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, amount=1n): Promise<{ success: boolean; token: number | null; }> {
+ async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {
return await super.burnToken(signer, collectionId, tokenId, amount);
}
/**
+ * Destroys a concrete instance of RFT on behalf of the owner.
+ * @param signer keyring of signer
+ * @param collectionId ID of collection
+ * @param tokenId ID of token
+ * @param fromAddressObj address on behalf of which the token will be burnt
+ * @param amount number of pieces to be burnt
+ * @example burnTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, 2n)
+ * @returns ```true``` if extrinsic success, otherwise ```false```
+ */
+ async burnTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {
+ return await super.burnTokenFrom(signer, collectionId, tokenId, fromAddressObj, amount);
+ }
+
+ /**
* Set, change, or remove approved address to transfer the ownership of the RFT.
*
* @param signer keyring of signer
@@ -1711,7 +1789,7 @@
* }, 18)
* @returns newly created fungible collection
*/
- async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, decimalPoints = 0): Promise<UniqueFTCollection> {
+ async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}, decimalPoints = 0): Promise<UniqueFTCollection> {
collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object
if(collectionOptions.tokenPropertyPermissions) throw Error('Fungible collections has no tokenPropertyPermissions');
collectionOptions.mode = {fungible: decimalPoints};
@@ -1776,8 +1854,8 @@
* @example getTop10Owners(10);
* @returns array of ```ICrossAccountId```
*/
- async getTop10Owners(collectionId: number): Promise<ICrossAccountId[]> {
- return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, 0])).toJSON().map(crossAccountIdFromLower);
+ async getTop10Owners(collectionId: number): Promise<CrossAccountId[]> {
+ return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, 0])).toJSON().map(CrossAccountId.fromLowerCaseKeys);
}
/**
@@ -1827,7 +1905,7 @@
* @returns ```true``` if extrinsic success, otherwise ```false```
*/
async burnTokens(signer: IKeyringPair, collectionId: number, amount=1n): Promise<boolean> {
- return (await super.burnToken(signer, collectionId, 0, amount)).success;
+ return await super.burnToken(signer, collectionId, 0, amount);
}
/**
@@ -1840,7 +1918,7 @@
* @returns ```true``` if extrinsic success, otherwise ```false```
*/
async burnTokensFrom(signer: IKeyringPair, collectionId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {
- return await super.burnTokenFrom(signer, collectionId, fromAddressObj, 0, amount);
+ return await super.burnTokenFrom(signer, collectionId, 0, fromAddressObj, amount);
}
/**
@@ -1936,7 +2014,7 @@
class BalanceGroup extends HelperGroup {
/**
- * Representation of the native token in the smallest unit
+ * Representation of the native token in the smallest unit - one OPAL (OPL), QUARTZ (QTZ), or UNIQUE (UNQ).
* @example getOneTokenNominal()
* @returns ```BigInt``` representation of the native token in the smallest unit, e.g. ```1_000_000_000_000_000_000n``` for QTZ.
*/
@@ -1996,9 +2074,9 @@
};
}
});
- let isSuccess = this.helper.address.normalizeSubstrate(typeof signer === 'string' ? signer : signer.address) === transfer.from;
- isSuccess = isSuccess && this.helper.address.normalizeSubstrate(address) === transfer.to;
- isSuccess = isSuccess && BigInt(amount) === transfer.amount;
+ const isSuccess = this.helper.address.normalizeSubstrate(typeof signer === 'string' ? signer : signer.address) === transfer.from
+ && this.helper.address.normalizeSubstrate(address) === transfer.to
+ && BigInt(amount) === transfer.amount;
return isSuccess;
}
}
@@ -2013,7 +2091,7 @@
* @returns substrate address converted to normalized (i.e., starting with 5) or specified explicitly representation
*/
normalizeSubstrate(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {
- return this.helper.util.normalizeSubstrateAddress(address, ss58Format);
+ return CrossAccountId.normalizeSubstrateAddress(address, ss58Format);
}
/**
@@ -2161,7 +2239,7 @@
}
-class UniqueCollectionBase {
+export class UniqueBaseCollection {
helper: UniqueHelper;
collectionId: number;
@@ -2194,6 +2272,14 @@
return await this.helper.collection.getEffectiveLimits(this.collectionId);
}
+ async getProperties(propertyKeys: string[] | null = null) {
+ return await this.helper.collection.getProperties(this.collectionId, propertyKeys);
+ }
+
+ async getTokenNextSponsored(tokenId: number, addressObj: ICrossAccountId) {
+ return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj);
+ }
+
async setSponsor(signer: TSigner, sponsorAddress: TSubstrateAccount) {
return await this.helper.collection.setSponsor(signer, this.collectionId, sponsorAddress);
}
@@ -2236,10 +2322,6 @@
async deleteProperties(signer: TSigner, propertyKeys: string[]) {
return await this.helper.collection.deleteProperties(signer, this.collectionId, propertyKeys);
- }
-
- async getTokenNextSponsored(tokenId: number, addressObj: ICrossAccountId) {
- return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj);
}
async setPermissions(signer: TSigner, permissions: ICollectionPermissions) {
@@ -2260,9 +2342,9 @@
}
-class UniqueNFTCollection extends UniqueCollectionBase {
+export class UniqueNFTCollection extends UniqueBaseCollection {
getTokenObject(tokenId: number) {
- return new UniqueNFTToken(tokenId, this);
+ return new UniqueNFToken(tokenId, this);
}
async getTokensByAddress(addressObj: ICrossAccountId) {
@@ -2285,6 +2367,14 @@
return await this.helper.nft.getTokenChildren(this.collectionId, tokenId, blockHashAt);
}
+ async getPropertyPermissions(propertyKeys: string[] | null = null) {
+ return await this.helper.nft.getPropertyPermissions(this.collectionId, propertyKeys);
+ }
+
+ async getTokenProperties(tokenId: number, propertyKeys: string[] | null = null) {
+ return await this.helper.nft.getTokenProperties(this.collectionId, tokenId, propertyKeys);
+ }
+
async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId) {
return await this.helper.nft.transferToken(signer, this.collectionId, tokenId, addressObj);
}
@@ -2313,6 +2403,10 @@
return await this.helper.nft.burnToken(signer, this.collectionId, tokenId);
}
+ async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId) {
+ return await this.helper.nft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj);
+ }
+
async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {
return await this.helper.nft.setTokenProperties(signer, this.collectionId, tokenId, properties);
}
@@ -2335,9 +2429,13 @@
}
-class UniqueRFTCollection extends UniqueCollectionBase {
+export class UniqueRFTCollection extends UniqueBaseCollection {
getTokenObject(tokenId: number) {
- return new UniqueRFTToken(tokenId, this);
+ return new UniqueRFToken(tokenId, this);
+ }
+
+ async getToken(tokenId: number, blockHashAt?: string) {
+ return await this.helper.rft.getToken(this.collectionId, tokenId, [], blockHashAt);
}
async getTokensByAddress(addressObj: ICrossAccountId) {
@@ -2356,6 +2454,18 @@
return await this.helper.rft.getTokenTotalPieces(this.collectionId, tokenId);
}
+ async getTokenApprovedPieces(tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {
+ return await this.helper.rft.getTokenApprovedPieces(this.collectionId, tokenId, toAddressObj, fromAddressObj);
+ }
+
+ async getPropertyPermissions(propertyKeys: string[] | null = null) {
+ return await this.helper.rft.getPropertyPermissions(this.collectionId, propertyKeys);
+ }
+
+ async getTokenProperties(tokenId: number, propertyKeys: string[] | null = null) {
+ return await this.helper.rft.getTokenProperties(this.collectionId, tokenId, propertyKeys);
+ }
+
async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId, amount=1n) {
return await this.helper.rft.transferToken(signer, this.collectionId, tokenId, addressObj, amount);
}
@@ -2366,10 +2476,6 @@
async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {
return await this.helper.rft.approveToken(signer, this.collectionId, tokenId, toAddressObj, amount);
- }
-
- async getTokenApprovedPieces(tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {
- return await this.helper.rft.getTokenApprovedPieces(this.collectionId, tokenId, toAddressObj, fromAddressObj);
}
async repartitionToken(signer: TSigner, tokenId: number, amount: bigint) {
@@ -2388,6 +2494,10 @@
return await this.helper.rft.burnToken(signer, this.collectionId, tokenId, amount);
}
+ async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n) {
+ return await this.helper.rft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, amount);
+ }
+
async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {
return await this.helper.rft.setTokenProperties(signer, this.collectionId, tokenId, properties);
}
@@ -2402,23 +2512,31 @@
}
-class UniqueFTCollection extends UniqueCollectionBase {
- async mint(signer: TSigner, amount=1n, owner: ICrossAccountId = {Substrate: signer.address}) {
- return await this.helper.ft.mintTokens(signer, this.collectionId, amount, owner);
+export class UniqueFTCollection extends UniqueBaseCollection {
+ async getBalance(addressObj: ICrossAccountId) {
+ return await this.helper.ft.getBalance(this.collectionId, addressObj);
}
- async mintWithOneOwner(signer: TSigner, tokens: {value: bigint}[], owner: ICrossAccountId = {Substrate: signer.address}) {
- return await this.helper.ft.mintMultipleTokensWithOneOwner(signer, this.collectionId, tokens, owner);
+ async getTotalPieces() {
+ return await this.helper.ft.getTotalPieces(this.collectionId);
}
- async getBalance(addressObj: ICrossAccountId) {
- return await this.helper.ft.getBalance(this.collectionId, addressObj);
+ async getApprovedTokens(fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {
+ return await this.helper.ft.getApprovedTokens(this.collectionId, fromAddressObj, toAddressObj);
}
async getTop10Owners() {
return await this.helper.ft.getTop10Owners(this.collectionId);
}
+ async mint(signer: TSigner, amount=1n, owner: ICrossAccountId = {Substrate: signer.address}) {
+ return await this.helper.ft.mintTokens(signer, this.collectionId, amount, owner);
+ }
+
+ async mintWithOneOwner(signer: TSigner, tokens: {value: bigint}[], owner: ICrossAccountId = {Substrate: signer.address}) {
+ return await this.helper.ft.mintMultipleTokensWithOneOwner(signer, this.collectionId, tokens, owner);
+ }
+
async transfer(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {
return await this.helper.ft.transfer(signer, this.collectionId, toAddressObj, amount);
}
@@ -2433,23 +2551,15 @@
async burnTokensFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) {
return await this.helper.ft.burnTokensFrom(signer, this.collectionId, fromAddressObj, amount);
- }
-
- async getTotalPieces() {
- return await this.helper.ft.getTotalPieces(this.collectionId);
}
async approveTokens(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {
return await this.helper.ft.approveTokens(signer, this.collectionId, toAddressObj, amount);
- }
-
- async getApprovedTokens(fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {
- return await this.helper.ft.getApprovedTokens(this.collectionId, fromAddressObj, toAddressObj);
}
}
-class UniqueTokenBase implements IToken {
+export class UniqueBaseToken {
collection: UniqueNFTCollection | UniqueRFTCollection;
collectionId: number;
tokenId: number;
@@ -2464,6 +2574,10 @@
return await this.collection.getTokenNextSponsored(this.tokenId, addressObj);
}
+ async getProperties(propertyKeys: string[] | null = null) {
+ return await this.collection.getTokenProperties(this.tokenId, propertyKeys);
+ }
+
async setProperties(signer: TSigner, properties: IProperty[]) {
return await this.collection.setTokenProperties(signer, this.tokenId, properties);
}
@@ -2471,10 +2585,14 @@
async deleteProperties(signer: TSigner, propertyKeys: string[]) {
return await this.collection.deleteTokenProperties(signer, this.tokenId, propertyKeys);
}
+
+ nestingAccount() {
+ return this.collection.helper.util.getTokenAccount(this);
+ }
}
-class UniqueNFTToken extends UniqueTokenBase {
+export class UniqueNFToken extends UniqueBaseToken {
collection: UniqueNFTCollection;
constructor(tokenId: number, collection: UniqueNFTCollection) {
@@ -2525,9 +2643,13 @@
async burn(signer: TSigner) {
return await this.collection.burnToken(signer, this.tokenId);
}
+
+ async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId) {
+ return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj);
+ }
}
-class UniqueRFTToken extends UniqueTokenBase {
+export class UniqueRFToken extends UniqueBaseToken {
collection: UniqueRFTCollection;
constructor(tokenId: number, collection: UniqueRFTCollection) {
@@ -2535,6 +2657,10 @@
this.collection = collection;
}
+ async getData(blockHashAt?: string) {
+ return await this.collection.getToken(this.tokenId, blockHashAt);
+ }
+
async getTop10Owners() {
return await this.collection.getTop10TokenOwners(this.tokenId);
}
@@ -2547,6 +2673,10 @@
return await this.collection.getTokenTotalPieces(this.tokenId);
}
+ async getApprovedPieces(fromAddressObj: ICrossAccountId, toAccountObj: ICrossAccountId) {
+ return await this.collection.getTokenApprovedPieces(this.tokenId, fromAddressObj, toAccountObj);
+ }
+
async transfer(signer: TSigner, addressObj: ICrossAccountId, amount=1n) {
return await this.collection.transferToken(signer, this.tokenId, addressObj, amount);
}
@@ -2559,10 +2689,6 @@
return await this.collection.approveToken(signer, this.tokenId, toAddressObj, amount);
}
- async getApprovedPieces(fromAddressObj: ICrossAccountId, toAccountObj: ICrossAccountId) {
- return await this.collection.getTokenApprovedPieces(this.tokenId, fromAddressObj, toAccountObj);
- }
-
async repartition(signer: TSigner, amount: bigint) {
return await this.collection.repartitionToken(signer, this.tokenId, amount);
}
@@ -2570,4 +2696,8 @@
async burn(signer: TSigner, amount=1n) {
return await this.collection.burnToken(signer, this.tokenId, amount);
}
+
+ async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) {
+ return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj, amount);
+ }
}