difftreelog
refactor EthCrossAccount impl, signature some evm methods, tests and stubs
in: master
16 files changed
pallets/common/src/erc.rsdiffbeforeafterboth--- a/pallets/common/src/erc.rs
+++ b/pallets/common/src/erc.rs
@@ -36,7 +36,7 @@
use crate::{
Pallet, CollectionHandle, Config, CollectionProperties, SelfWeightOf,
eth::{
- EthCrossAccount, convert_cross_account_to_uint256, CollectionPermissions as EvmPermissions,
+ EthCrossAccount, CollectionPermissions as EvmPermissions,
CollectionLimits as EvmCollectionLimits,
},
weights::WeightInfo,
pallets/common/src/eth.rsdiffbeforeafterboth--- a/pallets/common/src/eth.rs
+++ b/pallets/common/src/eth.rs
@@ -53,15 +53,6 @@
address[0..16] == ETH_COLLECTION_PREFIX
}
-/// Convert `CrossAccountId` to `uint256`.
-pub fn convert_cross_account_to_uint256<T: Config>(from: &T::CrossAccountId) -> uint256
-where
- T::AccountId: AsRef<[u8; 32]>,
-{
- let slice = from.as_sub().as_ref();
- uint256::from_big_endian(slice)
-}
-
/// Convert `uint256` to `CrossAccountId`.
pub fn convert_uint256_to_cross_account<T: Config>(from: uint256) -> T::CrossAccountId
where
@@ -71,22 +62,6 @@
from.to_big_endian(&mut new_admin_arr);
let account_id = T::AccountId::from(new_admin_arr);
T::CrossAccountId::from_sub(account_id)
-}
-
-/// Convert `CrossAccountId` to `(address, uint256)`.
-pub fn convert_cross_account_to_tuple<T: Config>(
- cross_account_id: &T::CrossAccountId,
-) -> (address, uint256)
-where
- T::AccountId: AsRef<[u8; 32]>,
-{
- if cross_account_id.is_canonical_substrate() {
- let sub = convert_cross_account_to_uint256::<T>(cross_account_id);
- (Default::default(), sub)
- } else {
- let eth = *cross_account_id.as_eth();
- (eth, Default::default())
- }
}
/// Convert tuple `(address, uint256)` to `CrossAccountId`.
@@ -128,10 +103,7 @@
T::AccountId: AsRef<[u8; 32]>,
{
if cross_account_id.is_canonical_substrate() {
- Self {
- eth: Default::default(),
- sub: convert_cross_account_to_uint256::<T>(cross_account_id),
- }
+ Self::from_sub::<T>(cross_account_id.as_sub())
} else {
Self {
eth: *cross_account_id.as_eth(),
pallets/evm-contract-helpers/Cargo.tomldiffbeforeafterboth--- a/pallets/evm-contract-helpers/Cargo.toml
+++ b/pallets/evm-contract-helpers/Cargo.toml
@@ -50,6 +50,7 @@
"pallet-evm-coder-substrate/std",
"pallet-evm/std",
"up-sponsorship/std",
+ "pallet-common/std",
]
try-runtime = ["frame-support/try-runtime"]
-stubgen = ["evm-coder/stubgen"]
+stubgen = ["evm-coder/stubgen", "pallet-common/stubgen"]
pallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth--- a/pallets/evm-contract-helpers/src/eth.rs
+++ b/pallets/evm-contract-helpers/src/eth.rs
@@ -175,18 +175,11 @@
///
/// @param contractAddress The contract for which a sponsor is requested.
/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
- fn sponsor(&self, contract_address: address) -> Result<(address, uint256)> {
- let sponsor =
- Pallet::<T>::get_sponsor(contract_address).ok_or("Contract has no sponsor")?;
- Ok(pallet_common::eth::convert_cross_account_to_tuple::<T>(
- &sponsor,
+ fn sponsor(&self, contract_address: address) -> Result<EthCrossAccount> {
+ Ok(EthCrossAccount::from_sub_cross_account::<T>(
+ &Pallet::<T>::get_sponsor(contract_address).ok_or("Contract has no sponsor")?,
))
}
- // fn sponsor(&self, contract_address: address) -> Result<EthCrossAccount> {
- // Ok(EthCrossAccount::from_sub_cross_account::<T>(
- // &Pallet::<T>::get_sponsor(contract_address).ok_or("Contract has no sponsor")?,
- // ))
- // }
/// Check tat contract has confirmed sponsor.
///
pallets/evm-contract-helpers/src/stubs/ContractHelpers.rawdiffbeforeafterbothbinary blob — no preview
pallets/evm-contract-helpers/src/stubs/ContractHelpers.soldiffbeforeafterboth--- a/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol
+++ b/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol
@@ -96,11 +96,11 @@
/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
/// @dev EVM selector for this function is: 0x766c4f37,
/// or in textual repr: sponsor(address)
- function sponsor(address contractAddress) public view returns (Tuple0 memory) {
+ function sponsor(address contractAddress) public view returns (EthCrossAccount memory) {
require(false, stub_error);
contractAddress;
dummy;
- return Tuple0(0x0000000000000000000000000000000000000000, 0);
+ return EthCrossAccount(0x0000000000000000000000000000000000000000, 0);
}
/// Check tat contract has confirmed sponsor.
@@ -265,8 +265,8 @@
}
}
-/// @dev anonymous struct
-struct Tuple0 {
- address field_0;
- uint256 field_1;
+/// @dev Cross account struct
+struct EthCrossAccount {
+ address eth;
+ uint256 sub;
}
pallets/nonfungible/src/erc.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -605,7 +605,7 @@
Ok(false)
}
- /// @notice Function to mint token.
+ /// @notice Function to a mint token.
/// @param to The new owner
/// @return uint256 The id of the newly minted token
#[weight(<SelfWeightOf<T>>::create_item())]
@@ -618,7 +618,7 @@
Ok(token_id)
}
- /// @notice Function to mint token.
+ /// @notice Function to a mint token.
/// @dev `tokenId` should be obtained with `nextTokenId` method,
/// unlike standard, you can't specify it manually
/// @param to The new owner
@@ -1070,7 +1070,7 @@
Ok(true)
}
- /// @notice Function to mint token.
+ /// @notice Function to a mint token.
/// @param to The new owner crossAccountId
/// @param properties Properties of minted token
/// @return uint256 The id of the newly minted token
pallets/nonfungible/src/stubs/UniqueNFT.soldiffbeforeafterboth--- a/pallets/nonfungible/src/stubs/UniqueNFT.sol
+++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol
@@ -738,7 +738,7 @@
return false;
}
- /// @notice Function to mint token.
+ /// @notice Function to a mint token.
/// @param to The new owner
/// @return uint256 The id of the newly minted token
/// @dev EVM selector for this function is: 0x6a627842,
@@ -750,7 +750,7 @@
return 0;
}
- // /// @notice Function to mint token.
+ // /// @notice Function to a mint token.
// /// @dev `tokenId` should be obtained with `nextTokenId` method,
// /// unlike standard, you can't specify it manually
// /// @param to The new owner
@@ -995,7 +995,7 @@
// return false;
// }
- /// @notice Function to mint token.
+ /// @notice Function to a mint token.
/// @param to The new owner crossAccountId
/// @param properties Properties of minted token
/// @return uint256 The id of the newly minted token
pallets/refungible/src/erc.rsdiffbeforeafterboth--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -637,7 +637,7 @@
Ok(false)
}
- /// @notice Function to mint token.
+ /// @notice Function to a mint token.
/// @param to The new owner
/// @return uint256 The id of the newly minted token
#[weight(<SelfWeightOf<T>>::create_item())]
@@ -650,7 +650,7 @@
Ok(token_id)
}
- /// @notice Function to mint token.
+ /// @notice Function to a mint token.
/// @dev `tokenId` should be obtained with `nextTokenId` method,
/// unlike standard, you can't specify it manually
/// @param to The new owner
@@ -1120,7 +1120,7 @@
Ok(true)
}
- /// @notice Function to mint token.
+ /// @notice Function to a mint token.
/// @param to The new owner crossAccountId
/// @param properties Properties of minted token
/// @return uint256 The id of the newly minted token
pallets/refungible/src/stubs/UniqueRefungible.soldiffbeforeafterboth--- a/pallets/refungible/src/stubs/UniqueRefungible.sol
+++ b/pallets/refungible/src/stubs/UniqueRefungible.sol
@@ -733,7 +733,7 @@
return false;
}
- /// @notice Function to mint token.
+ /// @notice Function to a mint token.
/// @param to The new owner
/// @return uint256 The id of the newly minted token
/// @dev EVM selector for this function is: 0x6a627842,
@@ -745,7 +745,7 @@
return 0;
}
- // /// @notice Function to mint token.
+ // /// @notice Function to a mint token.
// /// @dev `tokenId` should be obtained with `nextTokenId` method,
// /// unlike standard, you can't specify it manually
// /// @param to The new owner
@@ -979,7 +979,7 @@
// return false;
// }
- /// @notice Function to mint token.
+ /// @notice Function to a mint token.
/// @param to The new owner crossAccountId
/// @param properties Properties of minted token
/// @return uint256 The id of the newly minted token
tests/src/eth/abi/contractHelpers.jsondiffbeforeafterboth--- a/tests/src/eth/abi/contractHelpers.json
+++ b/tests/src/eth/abi/contractHelpers.json
@@ -223,10 +223,10 @@
"outputs": [
{
"components": [
- { "internalType": "address", "name": "field_0", "type": "address" },
- { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+ { "internalType": "address", "name": "eth", "type": "address" },
+ { "internalType": "uint256", "name": "sub", "type": "uint256" }
],
- "internalType": "struct Tuple0",
+ "internalType": "struct EthCrossAccount",
"name": "",
"type": "tuple"
}
tests/src/eth/api/ContractHelpers.soldiffbeforeafterboth--- a/tests/src/eth/api/ContractHelpers.sol
+++ b/tests/src/eth/api/ContractHelpers.sol
@@ -69,7 +69,7 @@
/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
/// @dev EVM selector for this function is: 0x766c4f37,
/// or in textual repr: sponsor(address)
- function sponsor(address contractAddress) external view returns (Tuple0 memory);
+ function sponsor(address contractAddress) external view returns (EthCrossAccount memory);
/// Check tat contract has confirmed sponsor.
///
@@ -171,8 +171,8 @@
function toggleAllowlist(address contractAddress, bool enabled) external;
}
-/// @dev anonymous struct
-struct Tuple0 {
- address field_0;
- uint256 field_1;
+/// @dev Cross account struct
+struct EthCrossAccount {
+ address eth;
+ uint256 sub;
}
tests/src/eth/api/UniqueNFT.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueNFT.sol
+++ b/tests/src/eth/api/UniqueNFT.sol
@@ -515,14 +515,14 @@
/// or in textual repr: mintingFinished()
function mintingFinished() external view returns (bool);
- /// @notice Function to mint token.
+ /// @notice Function to a mint token.
/// @param to The new owner
/// @return uint256 The id of the newly minted token
/// @dev EVM selector for this function is: 0x6a627842,
/// or in textual repr: mint(address)
function mint(address to) external returns (uint256);
- // /// @notice Function to mint token.
+ // /// @notice Function to a mint token.
// /// @dev `tokenId` should be obtained with `nextTokenId` method,
// /// unlike standard, you can't specify it manually
// /// @param to The new owner
@@ -674,7 +674,7 @@
// /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])
// function mintBulkWithTokenURI(address to, Tuple13[] memory tokens) external returns (bool);
- /// @notice Function to mint token.
+ /// @notice Function to a mint token.
/// @param to The new owner crossAccountId
/// @param properties Properties of minted token
/// @return uint256 The id of the newly minted token
tests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueRefungible.sol
+++ b/tests/src/eth/api/UniqueRefungible.sol
@@ -513,14 +513,14 @@
/// or in textual repr: mintingFinished()
function mintingFinished() external view returns (bool);
- /// @notice Function to mint token.
+ /// @notice Function to a mint token.
/// @param to The new owner
/// @return uint256 The id of the newly minted token
/// @dev EVM selector for this function is: 0x6a627842,
/// or in textual repr: mint(address)
function mint(address to) external returns (uint256);
- // /// @notice Function to mint token.
+ // /// @notice Function to a mint token.
// /// @dev `tokenId` should be obtained with `nextTokenId` method,
// /// unlike standard, you can't specify it manually
// /// @param to The new owner
@@ -666,7 +666,7 @@
// /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])
// function mintBulkWithTokenURI(address to, Tuple12[] memory tokens) external returns (bool);
- /// @notice Function to mint token.
+ /// @notice Function to a mint token.
/// @param to The new owner crossAccountId
/// @param properties Properties of minted token
/// @return uint256 The id of the newly minted token
tests/src/eth/nonFungible.test.tsdiffbeforeafterboth1// 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 {itEth, usingEthPlaygrounds, expect, EthUniqueHelper} from './util';18import {IKeyringPair} from '@polkadot/types/types';19import {Contract} from 'web3-eth-contract';20import exp from 'constants';21import {ITokenPropertyPermission} from '../util/playgrounds/types';222324describe('NFT: Information getting', () => {25 let donor: IKeyringPair;26 let alice: IKeyringPair;2728 before(async function() {29 await usingEthPlaygrounds(async (helper, privateKey) => {30 donor = await privateKey({filename: __filename});31 [alice] = await helper.arrange.createAccounts([10n], donor);32 });33 });3435 itEth('totalSupply', async ({helper}) => {36 const collection = await helper.nft.mintCollection(alice, {});37 await collection.mintToken(alice);3839 const caller = await helper.eth.createAccountWithBalance(donor);4041 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);42 const totalSupply = await contract.methods.totalSupply().call();4344 expect(totalSupply).to.equal('1');45 });4647 itEth('balanceOf', async ({helper}) => {48 const collection = await helper.nft.mintCollection(alice, {});49 const caller = await helper.eth.createAccountWithBalance(donor);5051 await collection.mintToken(alice, {Ethereum: caller});52 await collection.mintToken(alice, {Ethereum: caller});53 await collection.mintToken(alice, {Ethereum: caller});5455 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);56 const balance = await contract.methods.balanceOf(caller).call();5758 expect(balance).to.equal('3');59 });6061 itEth('ownerOf', async ({helper}) => {62 const collection = await helper.nft.mintCollection(alice, {});63 const caller = await helper.eth.createAccountWithBalance(donor);6465 const token = await collection.mintToken(alice, {Ethereum: caller});6667 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);6869 const owner = await contract.methods.ownerOf(token.tokenId).call();7071 expect(owner).to.equal(caller);72 });7374 itEth('name/symbol is available regardless of ERC721Metadata support', async ({helper}) => {75 const collection = await helper.nft.mintCollection(alice, {name: 'test', tokenPrefix: 'TEST'});76 const caller = helper.eth.createAccount();7778 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);7980 expect(await contract.methods.name().call()).to.equal('test');81 expect(await contract.methods.symbol().call()).to.equal('TEST');82 });83});8485describe('Check ERC721 token URI for NFT', () => {86 let donor: IKeyringPair;8788 before(async function() {89 await usingEthPlaygrounds(async (_helper, privateKey) => {90 donor = await privateKey({filename: __filename});91 });92 });9394 async function setup(helper: EthUniqueHelper, baseUri: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> {95 const owner = await helper.eth.createAccountWithBalance(donor);96 const receiver = helper.eth.createAccount();9798 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', 'a', 'b', baseUri);99 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);100101 const result = await contract.methods.mint(receiver).send();102 const tokenId = result.events.Transfer.returnValues.tokenId;103 expect(tokenId).to.be.equal('1');104105 if (propertyKey && propertyValue) {106 // Set URL or suffix107 await contract.methods.setProperties(tokenId, [{key: propertyKey, value: Buffer.from(propertyValue)}]).send();108 }109110 const event = result.events.Transfer;111 expect(event.address).to.be.equal(collectionAddress);112 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');113 expect(event.returnValues.to).to.be.equal(receiver);114 expect(event.returnValues.tokenId).to.be.equal(tokenId);115116 return {contract, nextTokenId: tokenId};117 }118119 itEth('Empty tokenURI', async ({helper}) => {120 const {contract, nextTokenId} = await setup(helper, '');121 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('');122 });123124 itEth('TokenURI from url', async ({helper}) => {125 const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URI', 'Token URI');126 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI');127 });128129 itEth('TokenURI from baseURI', async ({helper}) => {130 const {contract, nextTokenId} = await setup(helper, 'BaseURI_');131 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_');132 });133134 itEth('TokenURI from baseURI + suffix', async ({helper}) => {135 const suffix = '/some/suffix';136 const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URISuffix', suffix);137 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix);138 });139});140141describe('NFT: Plain calls', () => {142 let donor: IKeyringPair;143 let minter: IKeyringPair;144 let bob: IKeyringPair;145 let charlie: IKeyringPair;146147 before(async function() {148 await usingEthPlaygrounds(async (helper, privateKey) => {149 donor = await privateKey({filename: __filename});150 [minter, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);151 });152 });153154 itEth('Can perform mint() & get crossOwner()', async ({helper}) => {155 const owner = await helper.eth.createAccountWithBalance(donor);156 const receiver = helper.eth.createAccount();157158 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', '6', '6', '');159 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);160161 const result = await contract.methods.mintWithTokenURI(receiver, 'Test URI').send();162 const tokenId = result.events.Transfer.returnValues.tokenId;163 expect(tokenId).to.be.equal('1');164165 const event = result.events.Transfer;166 expect(event.address).to.be.equal(collectionAddress);167 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');168 expect(event.returnValues.to).to.be.equal(receiver);169170 expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');171 console.log(await contract.methods.crossOwnerOf(tokenId).call());172 expect(await contract.methods.crossOwnerOf(tokenId).call()).to.be.like([receiver, '0']);173 // TODO: this wont work right now, need release 919000 first174 // await helper.methods.setOffchainSchema(collectionIdAddress, 'https://offchain-service.local/token-info/{id}').send();175 // const tokenUri = await contract.methods.tokenURI(nextTokenId).call();176 // expect(tokenUri).to.be.equal(`https://offchain-service.local/token-info/${nextTokenId}`);177 });178 179 itEth('Can perform mintCross()', async ({helper}) => {180 const caller = await helper.eth.createAccountWithBalance(donor);181 const receiverCross = helper.ethCrossAccount.fromKeyringPair(bob);182 const properties = Array(5).fill(0).map((_, i) => { return {key: `key_${i}`, value: Buffer.from(`value_${i}`)}; });183 const permissions: ITokenPropertyPermission[] = properties.map(p => { return {key: p.key, permission: {tokenOwner: true,184 collectionAdmin: true,185 mutable: true}}; });186 187 188 const collection = await helper.nft.mintCollection(minter, {189 tokenPrefix: 'ethp',190 tokenPropertyPermissions: permissions,191 });192 await collection.addAdmin(minter, {Ethereum: caller});193 194 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);195 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', caller, true);196 let expectedTokenId = await contract.methods.nextTokenId().call();197 let result = await contract.methods.mintCross(receiverCross, []).send();198 let tokenId = result.events.Transfer.returnValues.tokenId;199 expect(tokenId).to.be.equal(expectedTokenId);200201 const event = result.events.Transfer;202 expect(event.address).to.be.equal(collectionAddress);203 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');204 expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(bob.address));205 expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);206 207 expectedTokenId = await contract.methods.nextTokenId().call();208 result = await contract.methods.mintCross(receiverCross, properties).send();209 tokenId = result.events.Transfer.returnValues.tokenId;210211 expect(tokenId).to.be.equal(expectedTokenId);212 213 expect(await contract.methods.properties(tokenId, []).call()).to.be.like(properties214 .map(p => { return helper.ethProperty.property(p.key, p.value.toString()); }));215 });216 217 //TODO: CORE-302 add eth methods218 itEth.skip('Can perform mintBulk()', async ({helper}) => {219 const caller = await helper.eth.createAccountWithBalance(donor);220 const receiver = helper.eth.createAccount();221222 const collection = await helper.nft.mintCollection(minter);223 await collection.addAdmin(minter, {Ethereum: caller});224225 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);226 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', caller);227 {228 const bulkSize = 3;229 const nextTokenId = await contract.methods.nextTokenId().call();230 expect(nextTokenId).to.be.equal('1');231 const result = await contract.methods.mintBulkWithTokenURI(232 receiver,233 Array.from({length: bulkSize}, (_, i) => (234 [+nextTokenId + i, `Test URI ${i}`]235 )),236 ).send({from: caller});237238 const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.tokenId - b.returnValues.tokenId);239 for (let i = 0; i < bulkSize; i++) {240 const event = events[i];241 expect(event.address).to.equal(collectionAddress);242 expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');243 expect(event.returnValues.to).to.equal(receiver);244 expect(event.returnValues.tokenId).to.equal(`${+nextTokenId+i}`);245246 expect(await contract.methods.tokenURI(+nextTokenId + i).call()).to.be.equal(`Test URI ${i}`);247 }248 }249 });250251 itEth('Can perform burn()', async ({helper}) => {252 const caller = await helper.eth.createAccountWithBalance(donor);253254 const collection = await helper.nft.mintCollection(minter, {});255 const {tokenId} = await collection.mintToken(minter, {Ethereum: caller});256257 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);258 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', caller);259260 {261 const result = await contract.methods.burn(tokenId).send({from: caller});262263 const event = result.events.Transfer;264 expect(event.address).to.be.equal(collectionAddress);265 expect(event.returnValues.from).to.be.equal(caller);266 expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000');267 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);268 }269 });270271 itEth('Can perform approve()', async ({helper}) => {272 const owner = await helper.eth.createAccountWithBalance(donor);273 const spender = helper.eth.createAccount();274275 const collection = await helper.nft.mintCollection(minter, {});276 const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});277278 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);279 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);280281 {282 const result = await contract.methods.approve(spender, tokenId).send({from: owner});283284 const event = result.events.Approval;285 expect(event.address).to.be.equal(collectionAddress);286 expect(event.returnValues.owner).to.be.equal(owner);287 expect(event.returnValues.approved).to.be.equal(spender);288 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);289 }290 });291292 itEth('Can perform setApprovalForAll()', async ({helper}) => {293 const owner = await helper.eth.createAccountWithBalance(donor);294 const operator = helper.eth.createAccount();295296 const collection = await helper.nft.mintCollection(minter, {});297298 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);299 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);300301 const approvedBefore = await contract.methods.isApprovedForAll(owner, operator).call();302 expect(approvedBefore).to.be.equal(false);303304 {305 const result = await contract.methods.setApprovalForAll(operator, true).send({from: owner});306307 expect(result.events.ApprovalForAll).to.be.like({308 address: collectionAddress,309 event: 'ApprovalForAll',310 returnValues: {311 owner,312 operator,313 approved: true,314 },315 });316317 const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();318 expect(approvedAfter).to.be.equal(true);319 }320321 {322 const result = await contract.methods.setApprovalForAll(operator, false).send({from: owner});323324 expect(result.events.ApprovalForAll).to.be.like({325 address: collectionAddress,326 event: 'ApprovalForAll',327 returnValues: {328 owner,329 operator,330 approved: false,331 },332 });333334 const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();335 expect(approvedAfter).to.be.equal(false);336 }337 });338339 itEth('Can perform burn with ApprovalForAll', async ({helper}) => {340 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});341342 const owner = await helper.eth.createAccountWithBalance(donor);343 const operator = await helper.eth.createAccountWithBalance(donor, 100n);344345 const token = await collection.mintToken(minter, {Ethereum: owner});346347 const address = helper.ethAddress.fromCollectionId(collection.collectionId);348 const contract = helper.ethNativeContract.collection(address, 'nft');349350 {351 await contract.methods.setApprovalForAll(operator, true).send({from: owner});352 const ownerCross = helper.ethCrossAccount.fromAddress(owner);353 const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: operator});354 const events = result.events.Transfer;355356 expect(events).to.be.like({357 address,358 event: 'Transfer',359 returnValues: {360 from: owner,361 to: '0x0000000000000000000000000000000000000000',362 tokenId: token.tokenId.toString(),363 },364 });365 }366 });367 368 itEth('Can perform transfer with ApprovalForAll', async ({helper}) => {369 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});370371 const owner = await helper.eth.createAccountWithBalance(donor);372 const operator = await helper.eth.createAccountWithBalance(donor);373 const receiver = charlie;374375 const token = await collection.mintToken(minter, {Ethereum: owner});376377 const address = helper.ethAddress.fromCollectionId(collection.collectionId);378 const contract = helper.ethNativeContract.collection(address, 'nft');379380 {381 await contract.methods.setApprovalForAll(operator, true).send({from: owner});382 const ownerCross = helper.ethCrossAccount.fromAddress(owner);383 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);384 const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: operator});385 const event = result.events.Transfer;386 expect(event).to.be.like({387 address: helper.ethAddress.fromCollectionId(collection.collectionId),388 event: 'Transfer',389 returnValues: {390 from: owner,391 to: helper.address.substrateToEth(receiver.address),392 tokenId: token.tokenId.toString(),393 },394 });395 }396397 expect(await token.getOwner()).to.be.like({Substrate: receiver.address});398 });399400 itEth('Can perform burnFromCross()', async ({helper}) => {401 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});402 const ownerSub = bob;403 const ownerCrossSub = helper.ethCrossAccount.fromKeyringPair(ownerSub);404 const ownerEth = await helper.eth.createAccountWithBalance(donor, 100n);405 const ownerCrossEth = helper.ethCrossAccount.fromAddress(ownerEth);406407 const burnerEth = await helper.eth.createAccountWithBalance(donor, 100n);408 const burnerCrossEth = helper.ethCrossAccount.fromAddress(burnerEth);409410 const token1 = await collection.mintToken(minter, {Substrate: ownerSub.address});411 const token2 = await collection.mintToken(minter, {Ethereum: ownerEth});412413 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);414 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft');415416 // Approve tokens from substrate and ethereum:417 await token1.approve(ownerSub, {Ethereum: burnerEth});418 await collectionEvm.methods.approveCross(burnerCrossEth, token2.tokenId).send({from: ownerEth});419420 // can burnFromCross:421 const result1 = await collectionEvm.methods.burnFromCross(ownerCrossSub, token1.tokenId).send({from: burnerEth});422 const result2 = await collectionEvm.methods.burnFromCross(ownerCrossEth, token2.tokenId).send({from: burnerEth});423 const events1 = result1.events.Transfer;424 const events2 = result2.events.Transfer;425426 // Check events for burnFromCross (substrate and ethereum):427 [428 [events1, token1, helper.address.substrateToEth(ownerSub.address)], 429 [events2, token2, ownerEth],430 ].map(burnData => {431 expect(burnData[0]).to.be.like({432 address: collectionAddress,433 event: 'Transfer',434 returnValues: {435 from: burnData[2],436 to: '0x0000000000000000000000000000000000000000',437 tokenId: burnData[1].tokenId.toString(),438 },439 });440 });441442 expect(await token1.doesExist()).to.be.false;443 expect(await token2.doesExist()).to.be.false;444 });445446 itEth('Can perform approveCross()', async ({helper}) => {447 // arrange: create accounts448 const owner = await helper.eth.createAccountWithBalance(donor, 100n);449 const ownerCross = helper.ethCrossAccount.fromAddress(owner);450 const receiverSub = charlie;451 const recieverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);452 const receiverEth = await helper.eth.createAccountWithBalance(donor, 100n);453 const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);454455 // arrange: create collection and tokens:456 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});457 const token1 = await collection.mintToken(minter, {Ethereum: owner});458 const token2 = await collection.mintToken(minter, {Ethereum: owner});459460 const collectionEvm = helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft');461462 // Can approveCross substrate and ethereum address:463 const resultSub = await collectionEvm.methods.approveCross(recieverCrossSub, token1.tokenId).send({from: owner});464 const resultEth = await collectionEvm.methods.approveCross(receiverCrossEth, token2.tokenId).send({from: owner});465 const eventSub = resultSub.events.Approval;466 const eventEth = resultEth.events.Approval;467 expect(eventSub).to.be.like({468 address: helper.ethAddress.fromCollectionId(collection.collectionId),469 event: 'Approval',470 returnValues: {471 owner,472 approved: helper.address.substrateToEth(receiverSub.address),473 tokenId: token1.tokenId.toString(),474 },475 });476 expect(eventEth).to.be.like({477 address: helper.ethAddress.fromCollectionId(collection.collectionId),478 event: 'Approval',479 returnValues: {480 owner,481 approved: receiverEth,482 tokenId: token2.tokenId.toString(),483 },484 });485486 // Substrate address can transferFrom approved tokens:487 await helper.nft.transferTokenFrom(receiverSub, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiverSub.address});488 expect(await helper.nft.getTokenOwner(collection.collectionId, token1.tokenId)).to.deep.eq({Substrate: receiverSub.address});489 // Ethereum address can transferFromCross approved tokens:490 await collectionEvm.methods.transferFromCross(ownerCross, receiverCrossEth, token2.tokenId).send({from: receiverEth});491 expect(await helper.nft.getTokenOwner(collection.collectionId, token2.tokenId)).to.deep.eq({Ethereum: receiverEth.toLowerCase()});492 });493494 itEth('Can reaffirm approved address', async ({helper}) => {495 const owner = await helper.eth.createAccountWithBalance(donor, 100n);496 const ownerCrossEth = helper.ethCrossAccount.fromAddress(owner);497 const [receiver1, receiver2] = await helper.arrange.createAccounts([100n, 100n], donor);498 const receiver1Cross = helper.ethCrossAccount.fromKeyringPair(receiver1);499 const receiver2Cross = helper.ethCrossAccount.fromKeyringPair(receiver2);500 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});501 const token1 = await collection.mintToken(minter, {Ethereum: owner});502 const token2 = await collection.mintToken(minter, {Ethereum: owner});503 const collectionEvm = helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft');504505 // Can approve and reaffirm approved address:506 await collectionEvm.methods.approveCross(receiver1Cross, token1.tokenId).send({from: owner});507 await collectionEvm.methods.approveCross(receiver2Cross, token1.tokenId).send({from: owner});508509 // receiver1 cannot transferFrom:510 await expect(helper.nft.transferTokenFrom(receiver1, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiver1.address})).to.be.rejected;511 // receiver2 can transferFrom:512 await helper.nft.transferTokenFrom(receiver2, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiver2.address});513514 // can set approved address to self address to remove approval:515 await collectionEvm.methods.approveCross(receiver1Cross, token2.tokenId).send({from: owner});516 await collectionEvm.methods.approveCross(ownerCrossEth, token2.tokenId).send({from: owner});517518 // receiver1 cannot transfer token anymore:519 await expect(helper.nft.transferTokenFrom(receiver1, collection.collectionId, token2.tokenId, {Ethereum: owner}, {Substrate: receiver1.address})).to.be.rejected;520 });521522 itEth('Can perform transferFrom()', async ({helper}) => {523 const owner = await helper.eth.createAccountWithBalance(donor);524 const spender = await helper.eth.createAccountWithBalance(donor);525 const receiver = helper.eth.createAccount();526527 const collection = await helper.nft.mintCollection(minter, {});528 const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});529530 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);531 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);532533 await contract.methods.approve(spender, tokenId).send({from: owner});534535 {536 const result = await contract.methods.transferFrom(owner, receiver, tokenId).send({from: spender});537538 const event = result.events.Transfer;539 expect(event.address).to.be.equal(collectionAddress);540 expect(event.returnValues.from).to.be.equal(owner);541 expect(event.returnValues.to).to.be.equal(receiver);542 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);543 }544545 {546 const balance = await contract.methods.balanceOf(receiver).call();547 expect(+balance).to.equal(1);548 }549550 {551 const balance = await contract.methods.balanceOf(owner).call();552 expect(+balance).to.equal(0);553 }554 });555556 itEth('Can perform transferFromCross()', async ({helper}) => {557 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});558559 const [owner, receiver] = await helper.arrange.createAccounts([100n, 100n], donor);560 const spender = await helper.eth.createAccountWithBalance(donor);561562 const token = await collection.mintToken(minter, {Substrate: owner.address});563564 const address = helper.ethAddress.fromCollectionId(collection.collectionId);565 const contract = helper.ethNativeContract.collection(address, 'nft');566567 await token.approve(owner, {Ethereum: spender});568569 {570 const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);571 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);572 const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender});573 const event = result.events.Transfer;574 expect(event).to.be.like({575 address: helper.ethAddress.fromCollectionId(collection.collectionId),576 event: 'Transfer',577 returnValues: {578 from: helper.address.substrateToEth(owner.address),579 to: helper.address.substrateToEth(receiver.address),580 tokenId: token.tokenId.toString(),581 },582 });583 }584585 expect(await token.getOwner()).to.be.like({Substrate: receiver.address});586 });587588 itEth('Can perform transfer()', async ({helper}) => {589 const collection = await helper.nft.mintCollection(minter, {});590 const owner = await helper.eth.createAccountWithBalance(donor);591 const receiver = helper.eth.createAccount();592593 const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});594595 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);596 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);597598 {599 const result = await contract.methods.transfer(receiver, tokenId).send({from: owner});600601 const event = result.events.Transfer;602 expect(event.address).to.be.equal(collectionAddress);603 expect(event.returnValues.from).to.be.equal(owner);604 expect(event.returnValues.to).to.be.equal(receiver);605 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);606 }607608 {609 const balance = await contract.methods.balanceOf(owner).call();610 expect(+balance).to.equal(0);611 }612613 {614 const balance = await contract.methods.balanceOf(receiver).call();615 expect(+balance).to.equal(1);616 }617 });618 619 itEth('Can perform transferCross()', async ({helper}) => {620 const collection = await helper.nft.mintCollection(minter, {});621 const owner = await helper.eth.createAccountWithBalance(donor);622 const receiverEth = await helper.eth.createAccountWithBalance(donor);623 const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);624 const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);625 626 const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});627628 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);629 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);630631 {632 // Can transferCross to ethereum address:633 const result = await collectionEvm.methods.transferCross(receiverCrossEth, tokenId).send({from: owner});634 // Check events:635 const event = result.events.Transfer;636 expect(event.address).to.be.equal(collectionAddress);637 expect(event.returnValues.from).to.be.equal(owner);638 expect(event.returnValues.to).to.be.equal(receiverEth);639 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);640 641 // owner has balance = 0:642 const ownerBalance = await collectionEvm.methods.balanceOf(owner).call();643 expect(+ownerBalance).to.equal(0);644 // receiver owns token:645 const receiverBalance = await collectionEvm.methods.balanceOf(receiverEth).call();646 expect(+receiverBalance).to.equal(1);647 expect(await helper.nft.getTokenOwner(collection.collectionId, tokenId)).to.deep.eq({Ethereum: receiverEth.toLowerCase()});648 }649 650 {651 // Can transferCross to substrate address:652 const substrateResult = await collectionEvm.methods.transferCross(receiverCrossSub, tokenId).send({from: receiverEth});653 // Check events:654 const event = substrateResult.events.Transfer;655 expect(event.address).to.be.equal(collectionAddress);656 expect(event.returnValues.from).to.be.equal(receiverEth);657 expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(minter.address));658 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);659 660 // owner has balance = 0:661 const ownerBalance = await collectionEvm.methods.balanceOf(receiverEth).call();662 expect(+ownerBalance).to.equal(0);663 // receiver owns token:664 const receiverBalance = await helper.nft.getTokensByAddress(collection.collectionId, {Substrate: minter.address});665 expect(receiverBalance).to.contain(tokenId);666 }667 });668669 ['transfer', 'transferCross'].map(testCase => itEth(`Cannot ${testCase} non-owned token`, async ({helper}) => {670 const sender = await helper.eth.createAccountWithBalance(donor);671 const tokenOwner = await helper.eth.createAccountWithBalance(donor);672 const receiverSub = minter;673 const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);674675 const collection = await helper.nft.mintCollection(minter, {});676 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);677 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', sender);678679 await collection.mintToken(minter, {Ethereum: sender});680 const nonSendersToken = await collection.mintToken(minter, {Ethereum: tokenOwner});681682 // Cannot transferCross someone else's token:683 const receiver = testCase === 'transfer' ? helper.address.substrateToEth(receiverSub.address) : receiverCrossSub;684 await expect(collectionEvm.methods[testCase](receiver, nonSendersToken.tokenId).send({from: sender})).to.be.rejected;685 // Cannot transfer token if it does not exist:686 await expect(collectionEvm.methods[testCase](receiver, 999999).send({from: sender})).to.be.rejected;687 }));688});689690describe('NFT: Fees', () => {691 let donor: IKeyringPair;692 let alice: IKeyringPair;693 let bob: IKeyringPair;694 let charlie: IKeyringPair;695696 before(async function() {697 await usingEthPlaygrounds(async (helper, privateKey) => {698 donor = await privateKey({filename: __filename});699 [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);700 });701 });702703 itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => {704 const owner = await helper.eth.createAccountWithBalance(donor);705 const spender = helper.eth.createAccount();706707 const collection = await helper.nft.mintCollection(alice, {});708 const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});709710 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);711712 const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, tokenId).send({from: owner}));713 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));714 });715716 itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {717 const owner = await helper.eth.createAccountWithBalance(donor);718 const spender = await helper.eth.createAccountWithBalance(donor);719720 const collection = await helper.nft.mintCollection(alice, {});721 const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});722723 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);724725 await contract.methods.approve(spender, tokenId).send({from: owner});726727 const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, tokenId).send({from: spender}));728 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));729 });730731 itEth('Can perform transferFromCross()', async ({helper}) => {732 const collectionMinter = alice;733 const owner = bob;734 const receiver = charlie;735 const collection = await helper.nft.mintCollection(collectionMinter, {name: 'A', description: 'B', tokenPrefix: 'C'});736737 const spender = await helper.eth.createAccountWithBalance(donor, 100n);738739 const token = await collection.mintToken(collectionMinter, {Substrate: owner.address});740741 const address = helper.ethAddress.fromCollectionId(collection.collectionId);742 const contract = helper.ethNativeContract.collection(address, 'nft');743744 await token.approve(owner, {Ethereum: spender});745746 {747 const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);748 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);749 const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender});750 const event = result.events.Transfer;751 expect(event).to.be.like({752 address: helper.ethAddress.fromCollectionId(collection.collectionId),753 event: 'Transfer',754 returnValues: {755 from: helper.address.substrateToEth(owner.address),756 to: helper.address.substrateToEth(receiver.address),757 tokenId: token.tokenId.toString(),758 },759 });760 }761762 expect(await token.getOwner()).to.be.like({Substrate: receiver.address});763 });764765 itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {766 const owner = await helper.eth.createAccountWithBalance(donor);767 const receiver = helper.eth.createAccount();768769 const collection = await helper.nft.mintCollection(alice, {});770 const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});771772 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);773774 const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, tokenId).send({from: owner}));775 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));776 });777});778779describe('NFT: Substrate calls', () => {780 let donor: IKeyringPair;781 let alice: IKeyringPair;782783 before(async function() {784 await usingEthPlaygrounds(async (helper, privateKey) => {785 donor = await privateKey({filename: __filename});786 [alice] = await helper.arrange.createAccounts([20n], donor);787 });788 });789790 itEth('Events emitted for mint()', async ({helper}) => {791 const collection = await helper.nft.mintCollection(alice, {});792 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);793 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');794795 const events: any = [];796 contract.events.allEvents((_: any, event: any) => {797 events.push(event);798 });799800 const {tokenId} = await collection.mintToken(alice);801 if (events.length == 0) await helper.wait.newBlocks(1);802 const event = events[0];803804 expect(event.event).to.be.equal('Transfer');805 expect(event.address).to.be.equal(collectionAddress);806 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');807 expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(alice.address));808 expect(event.returnValues.tokenId).to.be.equal(tokenId.toString());809 });810811 itEth('Events emitted for burn()', async ({helper}) => {812 const collection = await helper.nft.mintCollection(alice, {});813 const token = await collection.mintToken(alice);814815 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);816 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');817818 const events: any = [];819 contract.events.allEvents((_: any, event: any) => {820 events.push(event);821 });822823 await token.burn(alice);824 if (events.length == 0) await helper.wait.newBlocks(1);825 const event = events[0];826827 expect(event.event).to.be.equal('Transfer');828 expect(event.address).to.be.equal(collectionAddress);829 expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));830 expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000');831 expect(event.returnValues.tokenId).to.be.equal(token.tokenId.toString());832 });833834 itEth('Events emitted for approve()', async ({helper}) => {835 const receiver = helper.eth.createAccount();836837 const collection = await helper.nft.mintCollection(alice, {});838 const token = await collection.mintToken(alice);839840 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);841 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');842843 const events: any = [];844 contract.events.allEvents((_: any, event: any) => {845 events.push(event);846 });847848 await token.approve(alice, {Ethereum: receiver});849 if (events.length == 0) await helper.wait.newBlocks(1);850 const event = events[0];851852 expect(event.event).to.be.equal('Approval');853 expect(event.address).to.be.equal(collectionAddress);854 expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));855 expect(event.returnValues.approved).to.be.equal(receiver);856 expect(event.returnValues.tokenId).to.be.equal(token.tokenId.toString());857 });858859 itEth('Events emitted for transferFrom()', async ({helper}) => {860 const [bob] = await helper.arrange.createAccounts([10n], donor);861 const receiver = helper.eth.createAccount();862863 const collection = await helper.nft.mintCollection(alice, {});864 const token = await collection.mintToken(alice);865 await token.approve(alice, {Substrate: bob.address});866867 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);868 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');869870 const events: any = [];871 contract.events.allEvents((_: any, event: any) => {872 events.push(event);873 });874875 await token.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver});876877 if (events.length == 0) await helper.wait.newBlocks(1);878 const event = events[0];879880 expect(event.address).to.be.equal(collectionAddress);881 expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));882 expect(event.returnValues.to).to.be.equal(receiver);883 expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`);884 });885886 itEth('Events emitted for transfer()', async ({helper}) => {887 const receiver = helper.eth.createAccount();888889 const collection = await helper.nft.mintCollection(alice, {});890 const token = await collection.mintToken(alice);891892 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);893 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');894895 const events: any = [];896 contract.events.allEvents((_: any, event: any) => {897 events.push(event);898 });899900 await token.transfer(alice, {Ethereum: receiver});901902 if (events.length == 0) await helper.wait.newBlocks(1);903 const event = events[0];904905 expect(event.address).to.be.equal(collectionAddress);906 expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));907 expect(event.returnValues.to).to.be.equal(receiver);908 expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`);909 });910});911912describe('Common metadata', () => {913 let donor: IKeyringPair;914 let alice: IKeyringPair;915916 before(async function() {917 await usingEthPlaygrounds(async (helper, privateKey) => {918 donor = await privateKey({filename: __filename});919 [alice] = await helper.arrange.createAccounts([20n], donor);920 });921 });922923 itEth('Returns collection name', async ({helper}) => {924 const caller = await helper.eth.createAccountWithBalance(donor);925 const tokenPropertyPermissions = [{926 key: 'URI',927 permission: {928 mutable: true,929 collectionAdmin: true,930 tokenOwner: false,931 },932 }];933 const collection = await helper.nft.mintCollection(934 alice,935 {936 name: 'oh River',937 tokenPrefix: 'CHANGE',938 properties: [{key: 'ERC721Metadata', value: '1'}],939 tokenPropertyPermissions,940 },941 );942943 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);944 const name = await contract.methods.name().call();945 expect(name).to.equal('oh River');946 });947948 itEth('Returns symbol name', async ({helper}) => {949 const caller = await helper.eth.createAccountWithBalance(donor);950 const tokenPropertyPermissions = [{951 key: 'URI',952 permission: {953 mutable: true,954 collectionAdmin: true,955 tokenOwner: false,956 },957 }];958 const collection = await helper.nft.mintCollection(959 alice,960 {961 name: 'oh River',962 tokenPrefix: 'CHANGE',963 properties: [{key: 'ERC721Metadata', value: '1'}],964 tokenPropertyPermissions,965 },966 );967968 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);969 const symbol = await contract.methods.symbol().call();970 expect(symbol).to.equal('CHANGE');971 });972});973974describe('Negative tests', () => {975 let donor: IKeyringPair;976 let minter: IKeyringPair;977 let alice: IKeyringPair;978979 before(async function() {980 await usingEthPlaygrounds(async (helper, privateKey) => {981 donor = await privateKey({filename: __filename});982 [minter, alice] = await helper.arrange.createAccounts([100n, 100n], donor);983 });984 });985986 itEth('[negative] Cant perform burn without approval', async ({helper}) => {987 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});988989 const owner = await helper.eth.createAccountWithBalance(donor, 100n);990 const spender = await helper.eth.createAccountWithBalance(donor, 100n);991992 const token = await collection.mintToken(minter, {Ethereum: owner});993994 const address = helper.ethAddress.fromCollectionId(collection.collectionId);995 const contract = helper.ethNativeContract.collection(address, 'nft');996997 const ownerCross = helper.ethCrossAccount.fromAddress(owner);998 await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;9991000 await contract.methods.setApprovalForAll(spender, true).send({from: owner});1001 await contract.methods.setApprovalForAll(spender, false).send({from: owner});10021003 await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;1004 });10051006 itEth('[negative] Cant perform transfer without approval', async ({helper}) => {1007 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});1008 const receiver = alice;10091010 const owner = await helper.eth.createAccountWithBalance(donor, 100n);1011 const spender = await helper.eth.createAccountWithBalance(donor, 100n);10121013 const token = await collection.mintToken(minter, {Ethereum: owner});10141015 const address = helper.ethAddress.fromCollectionId(collection.collectionId);1016 const contract = helper.ethNativeContract.collection(address, 'nft');10171018 const ownerCross = helper.ethCrossAccount.fromAddress(owner);1019 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);10201021 await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;10221023 await contract.methods.setApprovalForAll(spender, true).send({from: owner});1024 await contract.methods.setApprovalForAll(spender, false).send({from: owner});1025 1026 await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;1027 });1028});tests/src/eth/reFungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/reFungible.test.ts
+++ b/tests/src/eth/reFungible.test.ts
@@ -17,7 +17,7 @@
import {Pallets, requirePalletsOrSkip} from '../util';
import {expect, itEth, usingEthPlaygrounds} from './util';
import {IKeyringPair} from '@polkadot/types/types';
-import { ITokenPropertyPermission } from '../util/playgrounds/types';
+import {ITokenPropertyPermission} from '../util/playgrounds/types';
describe('Refungible: Information getting', () => {
let donor: IKeyringPair;
@@ -159,7 +159,7 @@
let tokenId = result.events.Transfer.returnValues.tokenId;
expect(tokenId).to.be.equal(expectedTokenId);
- const event = result.events.Transfer;
+ let 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(helper.address.substrateToEth(bob.address));
@@ -167,6 +167,12 @@
expectedTokenId = await contract.methods.nextTokenId().call();
result = await contract.methods.mintCross(receiverCross, properties).send();
+ 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(helper.address.substrateToEth(bob.address));
+ expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);
+
tokenId = result.events.Transfer.returnValues.tokenId;
expect(tokenId).to.be.equal(expectedTokenId);