difftreelog
added `mintCross` function for `UniqueExtensoins` interfaces
in: master
20 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6085,7 +6085,7 @@
[[package]]
name = "pallet-fungible"
-version = "0.1.7"
+version = "0.1.9"
dependencies = [
"ethereum 0.14.0",
"evm-coder",
pallets/fungible/CHANGELOG.mddiffbeforeafterboth--- a/pallets/fungible/CHANGELOG.md
+++ b/pallets/fungible/CHANGELOG.md
@@ -4,6 +4,12 @@
<!-- bureaucrate goes here -->
+## [0.1.9] - 2022-12-01
+
+### Added
+
+- The functions `mintCross` to `ERC20UniqueExtensions` interface.
+
## [0.1.8] - 2022-11-18
### Added
pallets/fungible/Cargo.tomldiffbeforeafterboth--- a/pallets/fungible/Cargo.toml
+++ b/pallets/fungible/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "pallet-fungible"
-version = "0.1.7"
+version = "0.1.9"
license = "GPLv3"
edition = "2021"
pallets/fungible/src/erc.rsdiffbeforeafterboth--- a/pallets/fungible/src/erc.rs
+++ b/pallets/fungible/src/erc.rs
@@ -174,6 +174,19 @@
.collect::<string>())
}
+ #[weight(<SelfWeightOf<T>>::create_item())]
+ fn mint_cross(&mut self, caller: caller, to: EthCrossAccount, amount: uint256) -> Result<bool> {
+ let caller = T::CrossAccountId::from_eth(caller);
+ let to = to.into_sub_cross_account::<T>()?;
+ let amount = amount.try_into().map_err(|_| "amount overflow")?;
+ let budget = self
+ .recorder
+ .weight_calls_budget(<StructureWeight<T>>::find_parent());
+ <Pallet<T>>::create_item(&self, &caller, (to, amount), &budget)
+ .map_err(dispatch_to_evm::<T>)?;
+ Ok(true)
+ }
+
#[weight(<SelfWeightOf<T>>::approve())]
fn approve_cross(
&mut self,
pallets/fungible/src/stubs/UniqueFungible.soldiffbeforeafterboth--- a/pallets/fungible/src/stubs/UniqueFungible.sol
+++ b/pallets/fungible/src/stubs/UniqueFungible.sol
@@ -152,10 +152,10 @@
/// @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: 0x6ec0a9f1,
/// or in textual repr: collectionSponsor()
- function collectionSponsor() public view returns (Tuple8 memory) {
+ function collectionSponsor() public view returns (Tuple9 memory) {
require(false, stub_error);
dummy;
- return Tuple8(0x0000000000000000000000000000000000000000, 0);
+ return Tuple9(0x0000000000000000000000000000000000000000, 0);
}
/// Get current collection limits.
@@ -522,7 +522,7 @@
bytes value;
}
-/// @dev the ERC-165 identifier for this interface is 0x5b7038cf
+/// @dev the ERC-165 identifier for this interface is 0x7dee5997
contract ERC20UniqueExtensions is Dummy, ERC165 {
/// @notice A description for the collection.
/// @dev EVM selector for this function is: 0x7284e416,
@@ -533,6 +533,16 @@
return "";
}
+ /// @dev EVM selector for this function is: 0x269e6158,
+ /// or in textual repr: mintCross((address,uint256),uint256)
+ function mintCross(EthCrossAccount memory to, uint256 amount) public returns (bool) {
+ require(false, stub_error);
+ to;
+ amount;
+ dummy = 0;
+ return false;
+ }
+
/// @dev EVM selector for this function is: 0x0ecd0ab0,
/// or in textual repr: approveCross((address,uint256),uint256)
function approveCross(EthCrossAccount memory spender, uint256 amount) public returns (bool) {
@@ -577,7 +587,7 @@
/// @param amounts array of pairs of account address and amount
/// @dev EVM selector for this function is: 0x1acf2d55,
/// or in textual repr: mintBulk((address,uint256)[])
- function mintBulk(Tuple8[] memory amounts) public returns (bool) {
+ function mintBulk(Tuple9[] memory amounts) public returns (bool) {
require(false, stub_error);
amounts;
dummy = 0;
@@ -611,7 +621,7 @@
}
/// @dev anonymous struct
-struct Tuple8 {
+struct Tuple9 {
address field_0;
uint256 field_1;
}
pallets/nonfungible/CHANGELOG.mddiffbeforeafterboth--- a/pallets/nonfungible/CHANGELOG.md
+++ b/pallets/nonfungible/CHANGELOG.md
@@ -4,7 +4,7 @@
<!-- bureaucrate goes here -->
-## [0.1.11] - 2022-12-16
+## [0.1.12] - 2022-12-16
### Added
@@ -14,6 +14,12 @@
- Hide `setTokenPropertyPermission` function in `TokenProperties` interface.
+## [0.1.11] - 2022-12-01
+
+### Added
+
+- The functions `mintCross` to `ERC721UniqueExtensions` interface.
+
## [0.1.10] - 2022-11-18
### Added
pallets/nonfungible/src/erc.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -1069,6 +1069,58 @@
.map_err(dispatch_to_evm::<T>)?;
Ok(true)
}
+
+ /// @notice Function to mint token.
+ /// @param to The new owner crossAccountId
+ /// @param properties Properties of minted token
+ /// @return uint256 The id of the newly minted token
+ #[weight(<SelfWeightOf<T>>::create_item())]
+ fn mint_cross(
+ &mut self,
+ caller: caller,
+ to: EthCrossAccount,
+ properties: Vec<PropertyStruct>,
+ ) -> Result<uint256> {
+ let token_id = <TokensMinted<T>>::get(self.id)
+ .checked_add(1)
+ .ok_or("item id overflow")?;
+
+ let to = to.into_sub_cross_account::<T>()?;
+
+ let properties = properties
+ .into_iter()
+ .map(|PropertyStruct { key, value }| {
+ let key = <Vec<u8>>::from(key)
+ .try_into()
+ .map_err(|_| "key too large")?;
+
+ let value = value.0.try_into().map_err(|_| "value too large")?;
+
+ Ok(Property { key, value })
+ })
+ .collect::<Result<Vec<_>>>()?
+ .try_into()
+ .map_err(|_| Error::Revert(alloc::format!("too many properties")))?;
+
+ let caller = T::CrossAccountId::from_eth(caller);
+
+ let budget = self
+ .recorder
+ .weight_calls_budget(<StructureWeight<T>>::find_parent());
+
+ <Pallet<T>>::create_item(
+ self,
+ &caller,
+ CreateItemData::<T> {
+ properties,
+ owner: to,
+ },
+ &budget,
+ )
+ .map_err(dispatch_to_evm::<T>)?;
+
+ Ok(token_id.into())
+ }
}
#[solidity_interface(
pallets/nonfungible/src/stubs/UniqueNFT.soldiffbeforeafterboth--- a/pallets/nonfungible/src/stubs/UniqueNFT.sol
+++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol
@@ -290,10 +290,10 @@
/// @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: 0x6ec0a9f1,
/// or in textual repr: collectionSponsor()
- function collectionSponsor() public view returns (Tuple30 memory) {
+ function collectionSponsor() public view returns (Tuple32 memory) {
require(false, stub_error);
dummy;
- return Tuple30(0x0000000000000000000000000000000000000000, 0);
+ return Tuple32(0x0000000000000000000000000000000000000000, 0);
}
/// Get current collection limits.
@@ -655,7 +655,7 @@
}
/// @dev anonymous struct
-struct Tuple30 {
+struct Tuple32 {
address field_0;
uint256 field_1;
}
@@ -804,7 +804,7 @@
}
/// @title Unique extensions for ERC721.
-/// @dev the ERC-165 identifier for this interface is 0xb74c26b7
+/// @dev the ERC-165 identifier for this interface is 0x0e48fdb4
contract ERC721UniqueExtensions is Dummy, ERC165 {
/// @notice A descriptive name for a collection of NFTs in this contract
/// @dev EVM selector for this function is: 0x06fdde03,
@@ -961,6 +961,7 @@
dummy;
return 0;
}
+
// /// @notice Function to mint multiple tokens.
// /// @dev `tokenIds` should be an array of consecutive numbers and first number
// /// should be obtained with `nextTokenId` method
@@ -991,6 +992,19 @@
// return false;
// }
+ /// @notice Function to mint token.
+ /// @param to The new owner crossAccountId
+ /// @param properties Properties of minted token
+ /// @return uint256 The id of the newly minted token
+ /// @dev EVM selector for this function is: 0xb904db03,
+ /// or in textual repr: mintCross((address,uint256),(string,bytes)[])
+ function mintCross(EthCrossAccount memory to, Property[] memory properties) public returns (uint256) {
+ require(false, stub_error);
+ to;
+ properties;
+ dummy = 0;
+ return 0;
+ }
}
/// @dev anonymous struct
pallets/refungible/CHANGELOG.mddiffbeforeafterboth--- a/pallets/refungible/CHANGELOG.md
+++ b/pallets/refungible/CHANGELOG.md
@@ -4,7 +4,7 @@
<!-- bureaucrate goes here -->
-## [0.2.10] - 2022-12-16
+## [0.2.11] - 2022-12-16
### Added
@@ -14,6 +14,12 @@
- Hide `setTokenPropertyPermission` function in `TokenProperties` interface.
+## [0.2.10] - 2022-12-01
+
+### Added
+
+- The functions `mintCross` to `ERC721UniqueExtensions` interface.
+
## [0.2.9] - 2022-11-18
### Added
pallets/refungible/src/erc.rsdiffbeforeafterboth--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -1120,6 +1120,60 @@
Ok(true)
}
+ /// @notice Function to mint token.
+ /// @param to The new owner crossAccountId
+ /// @param properties Properties of minted token
+ /// @return uint256 The id of the newly minted token
+ #[weight(<SelfWeightOf<T>>::create_item())]
+ fn mint_cross(
+ &mut self,
+ caller: caller,
+ to: EthCrossAccount,
+ properties: Vec<PropertyStruct>,
+ ) -> Result<uint256> {
+ let token_id = <TokensMinted<T>>::get(self.id)
+ .checked_add(1)
+ .ok_or("item id overflow")?;
+
+ let to = to.into_sub_cross_account::<T>()?;
+
+ let properties = properties
+ .into_iter()
+ .map(|PropertyStruct { key, value }| {
+ let key = <Vec<u8>>::from(key)
+ .try_into()
+ .map_err(|_| "key too large")?;
+
+ let value = value.0.try_into().map_err(|_| "value too large")?;
+
+ Ok(Property { key, value })
+ })
+ .collect::<Result<Vec<_>>>()?
+ .try_into()
+ .map_err(|_| Error::Revert(alloc::format!("too many properties")))?;
+
+ let caller = T::CrossAccountId::from_eth(caller);
+
+ let budget = self
+ .recorder
+ .weight_calls_budget(<StructureWeight<T>>::find_parent());
+
+ let users = [(to, 1)]
+ .into_iter()
+ .collect::<BTreeMap<_, _>>()
+ .try_into()
+ .unwrap();
+ <Pallet<T>>::create_item(
+ self,
+ &caller,
+ CreateItemData::<T::CrossAccountId> { users, properties },
+ &budget,
+ )
+ .map_err(dispatch_to_evm::<T>)?;
+
+ Ok(token_id.into())
+ }
+
/// Returns EVM address for refungible token
///
/// @param token ID of the token
pallets/refungible/src/stubs/UniqueRefungible.soldiffbeforeafterboth--- a/pallets/refungible/src/stubs/UniqueRefungible.sol
+++ b/pallets/refungible/src/stubs/UniqueRefungible.sol
@@ -290,10 +290,10 @@
/// @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: 0x6ec0a9f1,
/// or in textual repr: collectionSponsor()
- function collectionSponsor() public view returns (Tuple29 memory) {
+ function collectionSponsor() public view returns (Tuple31 memory) {
require(false, stub_error);
dummy;
- return Tuple29(0x0000000000000000000000000000000000000000, 0);
+ return Tuple31(0x0000000000000000000000000000000000000000, 0);
}
/// Get current collection limits.
@@ -655,7 +655,7 @@
}
/// @dev anonymous struct
-struct Tuple29 {
+struct Tuple31 {
address field_0;
uint256 field_1;
}
@@ -802,7 +802,7 @@
}
/// @title Unique extensions for ERC721.
-/// @dev the ERC-165 identifier for this interface is 0x12f7d6c1
+/// @dev the ERC-165 identifier for this interface is 0xabf30dc2
contract ERC721UniqueExtensions is Dummy, ERC165 {
/// @notice A descriptive name for a collection of NFTs in this contract
/// @dev EVM selector for this function is: 0x06fdde03,
@@ -979,6 +979,20 @@
// return false;
// }
+ /// @notice Function to mint token.
+ /// @param to The new owner crossAccountId
+ /// @param properties Properties of minted token
+ /// @return uint256 The id of the newly minted token
+ /// @dev EVM selector for this function is: 0xb904db03,
+ /// or in textual repr: mintCross((address,uint256),(string,bytes)[])
+ function mintCross(EthCrossAccount memory to, Property[] memory properties) public returns (uint256) {
+ require(false, stub_error);
+ to;
+ properties;
+ dummy = 0;
+ return 0;
+ }
+
/// Returns EVM address for refungible token
///
/// @param token ID of the token
tests/src/eth/abi/fungible.jsondiffbeforeafterboth--- a/tests/src/eth/abi/fungible.json
+++ b/tests/src/eth/abi/fungible.json
@@ -322,7 +322,7 @@
{ "internalType": "address", "name": "field_0", "type": "address" },
{ "internalType": "uint256", "name": "field_1", "type": "uint256" }
],
- "internalType": "struct Tuple8",
+ "internalType": "struct Tuple9",
"name": "",
"type": "tuple"
}
@@ -408,7 +408,7 @@
{ "internalType": "address", "name": "field_0", "type": "address" },
{ "internalType": "uint256", "name": "field_1", "type": "uint256" }
],
- "internalType": "struct Tuple8[]",
+ "internalType": "struct Tuple9[]",
"name": "amounts",
"type": "tuple[]"
}
@@ -419,6 +419,24 @@
"type": "function"
},
{
+ "inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "eth", "type": "address" },
+ { "internalType": "uint256", "name": "sub", "type": "uint256" }
+ ],
+ "internalType": "struct EthCrossAccount",
+ "name": "to",
+ "type": "tuple"
+ },
+ { "internalType": "uint256", "name": "amount", "type": "uint256" }
+ ],
+ "name": "mintCross",
+ "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
"inputs": [],
"name": "name",
"outputs": [{ "internalType": "string", "name": "", "type": "string" }],
tests/src/eth/abi/nonFungible.jsondiffbeforeafterboth--- a/tests/src/eth/abi/nonFungible.json
+++ b/tests/src/eth/abi/nonFungible.json
@@ -352,7 +352,7 @@
{ "internalType": "address", "name": "field_0", "type": "address" },
{ "internalType": "uint256", "name": "field_1", "type": "uint256" }
],
- "internalType": "struct Tuple30",
+ "internalType": "struct Tuple32",
"name": "",
"type": "tuple"
}
@@ -478,6 +478,32 @@
},
{
"inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "eth", "type": "address" },
+ { "internalType": "uint256", "name": "sub", "type": "uint256" }
+ ],
+ "internalType": "struct EthCrossAccount",
+ "name": "to",
+ "type": "tuple"
+ },
+ {
+ "components": [
+ { "internalType": "string", "name": "key", "type": "string" },
+ { "internalType": "bytes", "name": "value", "type": "bytes" }
+ ],
+ "internalType": "struct Property[]",
+ "name": "properties",
+ "type": "tuple[]"
+ }
+ ],
+ "name": "mintCross",
+ "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
{ "internalType": "address", "name": "to", "type": "address" },
{ "internalType": "string", "name": "tokenUri", "type": "string" }
],
tests/src/eth/abi/reFungible.jsondiffbeforeafterboth--- a/tests/src/eth/abi/reFungible.json
+++ b/tests/src/eth/abi/reFungible.json
@@ -334,7 +334,7 @@
{ "internalType": "address", "name": "field_0", "type": "address" },
{ "internalType": "uint256", "name": "field_1", "type": "uint256" }
],
- "internalType": "struct Tuple29",
+ "internalType": "struct Tuple31",
"name": "",
"type": "tuple"
}
@@ -460,6 +460,32 @@
},
{
"inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "eth", "type": "address" },
+ { "internalType": "uint256", "name": "sub", "type": "uint256" }
+ ],
+ "internalType": "struct EthCrossAccount",
+ "name": "to",
+ "type": "tuple"
+ },
+ {
+ "components": [
+ { "internalType": "string", "name": "key", "type": "string" },
+ { "internalType": "bytes", "name": "value", "type": "bytes" }
+ ],
+ "internalType": "struct Property[]",
+ "name": "properties",
+ "type": "tuple[]"
+ }
+ ],
+ "name": "mintCross",
+ "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
{ "internalType": "address", "name": "to", "type": "address" },
{ "internalType": "string", "name": "tokenUri", "type": "string" }
],
tests/src/eth/api/UniqueFungible.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueFungible.sol
+++ b/tests/src/eth/api/UniqueFungible.sol
@@ -102,7 +102,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: 0x6ec0a9f1,
/// or in textual repr: collectionSponsor()
- function collectionSponsor() external view returns (Tuple8 memory);
+ function collectionSponsor() external view returns (Tuple9 memory);
/// Get current collection limits.
///
@@ -362,13 +362,17 @@
bytes value;
}
-/// @dev the ERC-165 identifier for this interface is 0x5b7038cf
+/// @dev the ERC-165 identifier for this interface is 0x7dee5997
interface ERC20UniqueExtensions is Dummy, ERC165 {
/// @notice A description for the collection.
/// @dev EVM selector for this function is: 0x7284e416,
/// or in textual repr: description()
function description() external view returns (string memory);
+ /// @dev EVM selector for this function is: 0x269e6158,
+ /// or in textual repr: mintCross((address,uint256),uint256)
+ function mintCross(EthCrossAccount memory to, uint256 amount) external returns (bool);
+
/// @dev EVM selector for this function is: 0x0ecd0ab0,
/// or in textual repr: approveCross((address,uint256),uint256)
function approveCross(EthCrossAccount memory spender, uint256 amount) external returns (bool);
@@ -395,7 +399,7 @@
/// @param amounts array of pairs of account address and amount
/// @dev EVM selector for this function is: 0x1acf2d55,
/// or in textual repr: mintBulk((address,uint256)[])
- function mintBulk(Tuple8[] memory amounts) external returns (bool);
+ function mintBulk(Tuple9[] memory amounts) external returns (bool);
/// @dev EVM selector for this function is: 0x2ada85ff,
/// or in textual repr: transferCross((address,uint256),uint256)
@@ -411,7 +415,7 @@
}
/// @dev anonymous struct
-struct Tuple8 {
+struct Tuple9 {
address field_0;
uint256 field_1;
}
tests/src/eth/api/UniqueNFT.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueNFT.sol
+++ b/tests/src/eth/api/UniqueNFT.sol
@@ -198,7 +198,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: 0x6ec0a9f1,
/// or in textual repr: collectionSponsor()
- function collectionSponsor() external view returns (Tuple27 memory);
+ function collectionSponsor() external view returns (Tuple29 memory);
/// Get current collection limits.
///
@@ -553,7 +553,7 @@
}
/// @title Unique extensions for ERC721.
-/// @dev the ERC-165 identifier for this interface is 0xb74c26b7
+/// @dev the ERC-165 identifier for this interface is 0x0e48fdb4
interface ERC721UniqueExtensions is Dummy, ERC165 {
/// @notice A descriptive name for a collection of NFTs in this contract
/// @dev EVM selector for this function is: 0x06fdde03,
@@ -652,6 +652,7 @@
/// @dev EVM selector for this function is: 0x75794a3c,
/// or in textual repr: nextTokenId()
function nextTokenId() external view returns (uint256);
+
// /// @notice Function to mint multiple tokens.
// /// @dev `tokenIds` should be an array of consecutive numbers and first number
// /// should be obtained with `nextTokenId` method
@@ -670,6 +671,13 @@
// /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])
// function mintBulkWithTokenURI(address to, Tuple13[] memory tokens) external returns (bool);
+ /// @notice Function to mint token.
+ /// @param to The new owner crossAccountId
+ /// @param properties Properties of minted token
+ /// @return uint256 The id of the newly minted token
+ /// @dev EVM selector for this function is: 0xb904db03,
+ /// or in textual repr: mintCross((address,uint256),(string,bytes)[])
+ function mintCross(EthCrossAccount memory to, Property[] memory properties) external returns (uint256);
}
/// @dev anonymous struct
tests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueRefungible.sol
+++ b/tests/src/eth/api/UniqueRefungible.sol
@@ -198,7 +198,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: 0x6ec0a9f1,
/// or in textual repr: collectionSponsor()
- function collectionSponsor() external view returns (Tuple26 memory);
+ function collectionSponsor() external view returns (Tuple28 memory);
/// Get current collection limits.
///
@@ -551,7 +551,7 @@
}
/// @title Unique extensions for ERC721.
-/// @dev the ERC-165 identifier for this interface is 0x12f7d6c1
+/// @dev the ERC-165 identifier for this interface is 0xabf30dc2
interface ERC721UniqueExtensions is Dummy, ERC165 {
/// @notice A descriptive name for a collection of NFTs in this contract
/// @dev EVM selector for this function is: 0x06fdde03,
@@ -663,6 +663,14 @@
// /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])
// function mintBulkWithTokenURI(address to, Tuple12[] memory tokens) external returns (bool);
+ /// @notice Function to mint token.
+ /// @param to The new owner crossAccountId
+ /// @param properties Properties of minted token
+ /// @return uint256 The id of the newly minted token
+ /// @dev EVM selector for this function is: 0xb904db03,
+ /// or in textual repr: mintCross((address,uint256),(string,bytes)[])
+ function mintCross(EthCrossAccount memory to, Property[] memory properties) external returns (uint256);
+
/// Returns EVM address for refungible token
///
/// @param token ID of the token
tests/src/eth/fungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/fungible.test.ts
+++ b/tests/src/eth/fungible.test.ts
@@ -78,6 +78,25 @@
expect(event.returnValues.to).to.equal(receiver);
expect(event.returnValues.value).to.equal('100');
});
+
+
+ itEth('Can perform mintCross()', async ({helper}) => {
+ const receiverCross = helper.ethCrossAccount.fromKeyringPair(owner);
+ const ethOwner = await helper.eth.createAccountWithBalance(donor);
+ const collection = await helper.ft.mintCollection(alice);
+ await collection.addAdmin(alice, {Ethereum: ethOwner});
+
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', ethOwner);
+
+ const result = await contract.methods.mintCross(receiverCross, 100).send();
+
+ const event = result.events.Transfer;
+ expect(event.address).to.equal(collectionAddress);
+ expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
+ expect(event.returnValues.to).to.equal(helper.address.substrateToEth(owner.address));
+ expect(event.returnValues.value).to.equal('100');
+ });
itEth('Can perform mintBulk()', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
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';202122describe('NFT: Information getting', () => {23 let donor: IKeyringPair;24 let alice: IKeyringPair;2526 before(async function() {27 await usingEthPlaygrounds(async (helper, privateKey) => {28 donor = await privateKey({filename: __filename});29 [alice] = await helper.arrange.createAccounts([10n], donor);30 });31 });3233 itEth('totalSupply', async ({helper}) => {34 const collection = await helper.nft.mintCollection(alice, {});35 await collection.mintToken(alice);3637 const caller = await helper.eth.createAccountWithBalance(donor);3839 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);40 const totalSupply = await contract.methods.totalSupply().call();4142 expect(totalSupply).to.equal('1');43 });4445 itEth('balanceOf', async ({helper}) => {46 const collection = await helper.nft.mintCollection(alice, {});47 const caller = await helper.eth.createAccountWithBalance(donor);4849 await collection.mintToken(alice, {Ethereum: caller});50 await collection.mintToken(alice, {Ethereum: caller});51 await collection.mintToken(alice, {Ethereum: caller});5253 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);54 const balance = await contract.methods.balanceOf(caller).call();5556 expect(balance).to.equal('3');57 });5859 itEth('ownerOf', async ({helper}) => {60 const collection = await helper.nft.mintCollection(alice, {});61 const caller = await helper.eth.createAccountWithBalance(donor);6263 const token = await collection.mintToken(alice, {Ethereum: caller});6465 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);6667 const owner = await contract.methods.ownerOf(token.tokenId).call();6869 expect(owner).to.equal(caller);70 });7172 itEth('name/symbol is available regardless of ERC721Metadata support', async ({helper}) => {73 const collection = await helper.nft.mintCollection(alice, {name: 'test', tokenPrefix: 'TEST'});74 const caller = helper.eth.createAccount();7576 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);7778 expect(await contract.methods.name().call()).to.equal('test');79 expect(await contract.methods.symbol().call()).to.equal('TEST');80 });81});8283describe('Check ERC721 token URI for NFT', () => {84 let donor: IKeyringPair;8586 before(async function() {87 await usingEthPlaygrounds(async (_helper, privateKey) => {88 donor = await privateKey({filename: __filename});89 });90 });9192 async function setup(helper: EthUniqueHelper, baseUri: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> {93 const owner = await helper.eth.createAccountWithBalance(donor);94 const receiver = helper.eth.createAccount();9596 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', 'a', 'b', baseUri);97 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);9899 const result = await contract.methods.mint(receiver).send();100 const tokenId = result.events.Transfer.returnValues.tokenId;101 expect(tokenId).to.be.equal('1');102103 if (propertyKey && propertyValue) {104 // Set URL or suffix105 await contract.methods.setProperties(tokenId, [{key: propertyKey, value: Buffer.from(propertyValue)}]).send();106 }107108 const event = result.events.Transfer;109 expect(event.address).to.be.equal(collectionAddress);110 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');111 expect(event.returnValues.to).to.be.equal(receiver);112 expect(event.returnValues.tokenId).to.be.equal(tokenId);113114 return {contract, nextTokenId: tokenId};115 }116117 itEth('Empty tokenURI', async ({helper}) => {118 const {contract, nextTokenId} = await setup(helper, '');119 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('');120 });121122 itEth('TokenURI from url', async ({helper}) => {123 const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URI', 'Token URI');124 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI');125 });126127 itEth('TokenURI from baseURI', async ({helper}) => {128 const {contract, nextTokenId} = await setup(helper, 'BaseURI_');129 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_');130 });131132 itEth('TokenURI from baseURI + suffix', async ({helper}) => {133 const suffix = '/some/suffix';134 const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URISuffix', suffix);135 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix);136 });137});138139describe('NFT: Plain calls', () => {140 let donor: IKeyringPair;141 let minter: IKeyringPair;142 let bob: IKeyringPair;143 let charlie: IKeyringPair;144145 before(async function() {146 await usingEthPlaygrounds(async (helper, privateKey) => {147 donor = await privateKey({filename: __filename});148 [minter, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);149 });150 });151152 itEth('Can perform mint() & get crossOwner()', async ({helper}) => {153 const owner = await helper.eth.createAccountWithBalance(donor);154 const receiver = helper.eth.createAccount();155156 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', '6', '6', '');157 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);158159 const result = await contract.methods.mintWithTokenURI(receiver, 'Test URI').send();160 const tokenId = result.events.Transfer.returnValues.tokenId;161 expect(tokenId).to.be.equal('1');162163 const event = result.events.Transfer;164 expect(event.address).to.be.equal(collectionAddress);165 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');166 expect(event.returnValues.to).to.be.equal(receiver);167168 expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');169 console.log(await contract.methods.crossOwnerOf(tokenId).call());170 expect(await contract.methods.crossOwnerOf(tokenId).call()).to.be.like([receiver, '0']);171 // TODO: this wont work right now, need release 919000 first172 // await helper.methods.setOffchainSchema(collectionIdAddress, 'https://offchain-service.local/token-info/{id}').send();173 // const tokenUri = await contract.methods.tokenURI(nextTokenId).call();174 // expect(tokenUri).to.be.equal(`https://offchain-service.local/token-info/${nextTokenId}`);175 });176177 //TODO: CORE-302 add eth methods178 itEth.skip('Can perform mintBulk()', async ({helper}) => {179 const caller = await helper.eth.createAccountWithBalance(donor);180 const receiver = helper.eth.createAccount();181182 const collection = await helper.nft.mintCollection(minter);183 await collection.addAdmin(minter, {Ethereum: caller});184185 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);186 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', caller);187 {188 const bulkSize = 3;189 const nextTokenId = await contract.methods.nextTokenId().call();190 expect(nextTokenId).to.be.equal('1');191 const result = await contract.methods.mintBulkWithTokenURI(192 receiver,193 Array.from({length: bulkSize}, (_, i) => (194 [+nextTokenId + i, `Test URI ${i}`]195 )),196 ).send({from: caller});197198 const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.tokenId - b.returnValues.tokenId);199 for (let i = 0; i < bulkSize; i++) {200 const event = events[i];201 expect(event.address).to.equal(collectionAddress);202 expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');203 expect(event.returnValues.to).to.equal(receiver);204 expect(event.returnValues.tokenId).to.equal(`${+nextTokenId+i}`);205206 expect(await contract.methods.tokenURI(+nextTokenId + i).call()).to.be.equal(`Test URI ${i}`);207 }208 }209 });210211 itEth('Can perform burn()', async ({helper}) => {212 const caller = await helper.eth.createAccountWithBalance(donor);213214 const collection = await helper.nft.mintCollection(minter, {});215 const {tokenId} = await collection.mintToken(minter, {Ethereum: caller});216217 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);218 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', caller);219220 {221 const result = await contract.methods.burn(tokenId).send({from: caller});222223 const event = result.events.Transfer;224 expect(event.address).to.be.equal(collectionAddress);225 expect(event.returnValues.from).to.be.equal(caller);226 expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000');227 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);228 }229 });230231 itEth('Can perform approve()', async ({helper}) => {232 const owner = await helper.eth.createAccountWithBalance(donor);233 const spender = helper.eth.createAccount();234235 const collection = await helper.nft.mintCollection(minter, {});236 const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});237238 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);239 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);240241 {242 const result = await contract.methods.approve(spender, tokenId).send({from: owner});243244 const event = result.events.Approval;245 expect(event.address).to.be.equal(collectionAddress);246 expect(event.returnValues.owner).to.be.equal(owner);247 expect(event.returnValues.approved).to.be.equal(spender);248 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);249 }250 });251252 itEth('Can perform setApprovalForAll()', async ({helper}) => {253 const owner = await helper.eth.createAccountWithBalance(donor);254 const operator = helper.eth.createAccount();255256 const collection = await helper.nft.mintCollection(minter, {});257258 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);259 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);260261 const approvedBefore = await contract.methods.isApprovedForAll(owner, operator).call();262 expect(approvedBefore).to.be.equal(false);263264 {265 const result = await contract.methods.setApprovalForAll(operator, true).send({from: owner});266267 expect(result.events.ApprovalForAll).to.be.like({268 address: collectionAddress,269 event: 'ApprovalForAll',270 returnValues: {271 owner,272 operator,273 approved: true,274 },275 });276277 const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();278 expect(approvedAfter).to.be.equal(true);279 }280281 {282 const result = await contract.methods.setApprovalForAll(operator, false).send({from: owner});283284 expect(result.events.ApprovalForAll).to.be.like({285 address: collectionAddress,286 event: 'ApprovalForAll',287 returnValues: {288 owner,289 operator,290 approved: false,291 },292 });293294 const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();295 expect(approvedAfter).to.be.equal(false);296 }297 });298299 itEth('Can perform burn with ApprovalForAll', async ({helper}) => {300 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});301302 const owner = await helper.eth.createAccountWithBalance(donor);303 const operator = await helper.eth.createAccountWithBalance(donor, 100n);304305 const token = await collection.mintToken(minter, {Ethereum: owner});306307 const address = helper.ethAddress.fromCollectionId(collection.collectionId);308 const contract = helper.ethNativeContract.collection(address, 'nft');309310 {311 await contract.methods.setApprovalForAll(operator, true).send({from: owner});312 const ownerCross = helper.ethCrossAccount.fromAddress(owner);313 const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: operator});314 const events = result.events.Transfer;315316 expect(events).to.be.like({317 address,318 event: 'Transfer',319 returnValues: {320 from: owner,321 to: '0x0000000000000000000000000000000000000000',322 tokenId: token.tokenId.toString(),323 },324 });325 }326 });327 328 itEth('Can perform transfer with ApprovalForAll', async ({helper}) => {329 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});330331 const owner = await helper.eth.createAccountWithBalance(donor);332 const operator = await helper.eth.createAccountWithBalance(donor);333 const receiver = charlie;334335 const token = await collection.mintToken(minter, {Ethereum: owner});336337 const address = helper.ethAddress.fromCollectionId(collection.collectionId);338 const contract = helper.ethNativeContract.collection(address, 'nft');339340 {341 await contract.methods.setApprovalForAll(operator, true).send({from: owner});342 const ownerCross = helper.ethCrossAccount.fromAddress(owner);343 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);344 const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: operator});345 const event = result.events.Transfer;346 expect(event).to.be.like({347 address: helper.ethAddress.fromCollectionId(collection.collectionId),348 event: 'Transfer',349 returnValues: {350 from: owner,351 to: helper.address.substrateToEth(receiver.address),352 tokenId: token.tokenId.toString(),353 },354 });355 }356357 expect(await token.getOwner()).to.be.like({Substrate: receiver.address});358 });359360 itEth('Can perform burnFromCross()', async ({helper}) => {361 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});362 const ownerSub = bob;363 const ownerCrossSub = helper.ethCrossAccount.fromKeyringPair(ownerSub);364 const ownerEth = await helper.eth.createAccountWithBalance(donor, 100n);365 const ownerCrossEth = helper.ethCrossAccount.fromAddress(ownerEth);366367 const burnerEth = await helper.eth.createAccountWithBalance(donor, 100n);368 const burnerCrossEth = helper.ethCrossAccount.fromAddress(burnerEth);369370 const token1 = await collection.mintToken(minter, {Substrate: ownerSub.address});371 const token2 = await collection.mintToken(minter, {Ethereum: ownerEth});372373 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);374 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft');375376 // Approve tokens from substrate and ethereum:377 await token1.approve(ownerSub, {Ethereum: burnerEth});378 await collectionEvm.methods.approveCross(burnerCrossEth, token2.tokenId).send({from: ownerEth});379380 // can burnFromCross:381 const result1 = await collectionEvm.methods.burnFromCross(ownerCrossSub, token1.tokenId).send({from: burnerEth});382 const result2 = await collectionEvm.methods.burnFromCross(ownerCrossEth, token2.tokenId).send({from: burnerEth});383 const events1 = result1.events.Transfer;384 const events2 = result2.events.Transfer;385386 // Check events for burnFromCross (substrate and ethereum):387 [388 [events1, token1, helper.address.substrateToEth(ownerSub.address)], 389 [events2, token2, ownerEth],390 ].map(burnData => {391 expect(burnData[0]).to.be.like({392 address: collectionAddress,393 event: 'Transfer',394 returnValues: {395 from: burnData[2],396 to: '0x0000000000000000000000000000000000000000',397 tokenId: burnData[1].tokenId.toString(),398 },399 });400 });401402 expect(await token1.doesExist()).to.be.false;403 expect(await token2.doesExist()).to.be.false;404 });405406 itEth('Can perform approveCross()', async ({helper}) => {407 // arrange: create accounts408 const owner = await helper.eth.createAccountWithBalance(donor, 100n);409 const ownerCross = helper.ethCrossAccount.fromAddress(owner);410 const receiverSub = charlie;411 const recieverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);412 const receiverEth = await helper.eth.createAccountWithBalance(donor, 100n);413 const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);414415 // arrange: create collection and tokens:416 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});417 const token1 = await collection.mintToken(minter, {Ethereum: owner});418 const token2 = await collection.mintToken(minter, {Ethereum: owner});419420 const collectionEvm = helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft');421422 // Can approveCross substrate and ethereum address:423 const resultSub = await collectionEvm.methods.approveCross(recieverCrossSub, token1.tokenId).send({from: owner});424 const resultEth = await collectionEvm.methods.approveCross(receiverCrossEth, token2.tokenId).send({from: owner});425 const eventSub = resultSub.events.Approval;426 const eventEth = resultEth.events.Approval;427 expect(eventSub).to.be.like({428 address: helper.ethAddress.fromCollectionId(collection.collectionId),429 event: 'Approval',430 returnValues: {431 owner,432 approved: helper.address.substrateToEth(receiverSub.address),433 tokenId: token1.tokenId.toString(),434 },435 });436 expect(eventEth).to.be.like({437 address: helper.ethAddress.fromCollectionId(collection.collectionId),438 event: 'Approval',439 returnValues: {440 owner,441 approved: receiverEth,442 tokenId: token2.tokenId.toString(),443 },444 });445446 // Substrate address can transferFrom approved tokens:447 await helper.nft.transferTokenFrom(receiverSub, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiverSub.address});448 expect(await helper.nft.getTokenOwner(collection.collectionId, token1.tokenId)).to.deep.eq({Substrate: receiverSub.address});449 // Ethereum address can transferFromCross approved tokens:450 await collectionEvm.methods.transferFromCross(ownerCross, receiverCrossEth, token2.tokenId).send({from: receiverEth});451 expect(await helper.nft.getTokenOwner(collection.collectionId, token2.tokenId)).to.deep.eq({Ethereum: receiverEth.toLowerCase()});452 });453454 itEth('Can reaffirm approved address', async ({helper}) => {455 const owner = await helper.eth.createAccountWithBalance(donor, 100n);456 const ownerCrossEth = helper.ethCrossAccount.fromAddress(owner);457 const [receiver1, receiver2] = await helper.arrange.createAccounts([100n, 100n], donor);458 const receiver1Cross = helper.ethCrossAccount.fromKeyringPair(receiver1);459 const receiver2Cross = helper.ethCrossAccount.fromKeyringPair(receiver2);460 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});461 const token1 = await collection.mintToken(minter, {Ethereum: owner});462 const token2 = await collection.mintToken(minter, {Ethereum: owner});463 const collectionEvm = helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft');464465 // Can approve and reaffirm approved address:466 await collectionEvm.methods.approveCross(receiver1Cross, token1.tokenId).send({from: owner});467 await collectionEvm.methods.approveCross(receiver2Cross, token1.tokenId).send({from: owner});468469 // receiver1 cannot transferFrom:470 await expect(helper.nft.transferTokenFrom(receiver1, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiver1.address})).to.be.rejected;471 // receiver2 can transferFrom:472 await helper.nft.transferTokenFrom(receiver2, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiver2.address});473474 // can set approved address to self address to remove approval:475 await collectionEvm.methods.approveCross(receiver1Cross, token2.tokenId).send({from: owner});476 await collectionEvm.methods.approveCross(ownerCrossEth, token2.tokenId).send({from: owner});477478 // receiver1 cannot transfer token anymore:479 await expect(helper.nft.transferTokenFrom(receiver1, collection.collectionId, token2.tokenId, {Ethereum: owner}, {Substrate: receiver1.address})).to.be.rejected;480 });481482 itEth('Can perform transferFrom()', async ({helper}) => {483 const owner = await helper.eth.createAccountWithBalance(donor);484 const spender = await helper.eth.createAccountWithBalance(donor);485 const receiver = helper.eth.createAccount();486487 const collection = await helper.nft.mintCollection(minter, {});488 const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});489490 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);491 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);492493 await contract.methods.approve(spender, tokenId).send({from: owner});494495 {496 const result = await contract.methods.transferFrom(owner, receiver, tokenId).send({from: spender});497498 const event = result.events.Transfer;499 expect(event.address).to.be.equal(collectionAddress);500 expect(event.returnValues.from).to.be.equal(owner);501 expect(event.returnValues.to).to.be.equal(receiver);502 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);503 }504505 {506 const balance = await contract.methods.balanceOf(receiver).call();507 expect(+balance).to.equal(1);508 }509510 {511 const balance = await contract.methods.balanceOf(owner).call();512 expect(+balance).to.equal(0);513 }514 });515516 itEth('Can perform transferFromCross()', async ({helper}) => {517 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});518519 const [owner, receiver] = await helper.arrange.createAccounts([100n, 100n], donor);520 const spender = await helper.eth.createAccountWithBalance(donor);521522 const token = await collection.mintToken(minter, {Substrate: owner.address});523524 const address = helper.ethAddress.fromCollectionId(collection.collectionId);525 const contract = helper.ethNativeContract.collection(address, 'nft');526527 await token.approve(owner, {Ethereum: spender});528529 {530 const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);531 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);532 const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender});533 const event = result.events.Transfer;534 expect(event).to.be.like({535 address: helper.ethAddress.fromCollectionId(collection.collectionId),536 event: 'Transfer',537 returnValues: {538 from: helper.address.substrateToEth(owner.address),539 to: helper.address.substrateToEth(receiver.address),540 tokenId: token.tokenId.toString(),541 },542 });543 }544545 expect(await token.getOwner()).to.be.like({Substrate: receiver.address});546 });547548 itEth('Can perform transfer()', async ({helper}) => {549 const collection = await helper.nft.mintCollection(minter, {});550 const owner = await helper.eth.createAccountWithBalance(donor);551 const receiver = helper.eth.createAccount();552553 const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});554555 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);556 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);557558 {559 const result = await contract.methods.transfer(receiver, tokenId).send({from: owner});560561 const event = result.events.Transfer;562 expect(event.address).to.be.equal(collectionAddress);563 expect(event.returnValues.from).to.be.equal(owner);564 expect(event.returnValues.to).to.be.equal(receiver);565 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);566 }567568 {569 const balance = await contract.methods.balanceOf(owner).call();570 expect(+balance).to.equal(0);571 }572573 {574 const balance = await contract.methods.balanceOf(receiver).call();575 expect(+balance).to.equal(1);576 }577 });578 579 itEth('Can perform transferCross()', async ({helper}) => {580 const collection = await helper.nft.mintCollection(minter, {});581 const owner = await helper.eth.createAccountWithBalance(donor);582 const receiverEth = await helper.eth.createAccountWithBalance(donor);583 const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);584 const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);585 586 const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});587588 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);589 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);590591 {592 // Can transferCross to ethereum address:593 const result = await collectionEvm.methods.transferCross(receiverCrossEth, tokenId).send({from: owner});594 // Check events:595 const event = result.events.Transfer;596 expect(event.address).to.be.equal(collectionAddress);597 expect(event.returnValues.from).to.be.equal(owner);598 expect(event.returnValues.to).to.be.equal(receiverEth);599 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);600 601 // owner has balance = 0:602 const ownerBalance = await collectionEvm.methods.balanceOf(owner).call();603 expect(+ownerBalance).to.equal(0);604 // receiver owns token:605 const receiverBalance = await collectionEvm.methods.balanceOf(receiverEth).call();606 expect(+receiverBalance).to.equal(1);607 expect(await helper.nft.getTokenOwner(collection.collectionId, tokenId)).to.deep.eq({Ethereum: receiverEth.toLowerCase()});608 }609 610 {611 // Can transferCross to substrate address:612 const substrateResult = await collectionEvm.methods.transferCross(receiverCrossSub, tokenId).send({from: receiverEth});613 // Check events:614 const event = substrateResult.events.Transfer;615 expect(event.address).to.be.equal(collectionAddress);616 expect(event.returnValues.from).to.be.equal(receiverEth);617 expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(minter.address));618 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);619 620 // owner has balance = 0:621 const ownerBalance = await collectionEvm.methods.balanceOf(receiverEth).call();622 expect(+ownerBalance).to.equal(0);623 // receiver owns token:624 const receiverBalance = await helper.nft.getTokensByAddress(collection.collectionId, {Substrate: minter.address});625 expect(receiverBalance).to.contain(tokenId);626 }627 });628629 ['transfer', 'transferCross'].map(testCase => itEth(`Cannot ${testCase} non-owned token`, async ({helper}) => {630 const sender = await helper.eth.createAccountWithBalance(donor);631 const tokenOwner = await helper.eth.createAccountWithBalance(donor);632 const receiverSub = minter;633 const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);634635 const collection = await helper.nft.mintCollection(minter, {});636 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);637 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', sender);638639 await collection.mintToken(minter, {Ethereum: sender});640 const nonSendersToken = await collection.mintToken(minter, {Ethereum: tokenOwner});641642 // Cannot transferCross someone else's token:643 const receiver = testCase === 'transfer' ? helper.address.substrateToEth(receiverSub.address) : receiverCrossSub;644 await expect(collectionEvm.methods[testCase](receiver, nonSendersToken.tokenId).send({from: sender})).to.be.rejected;645 // Cannot transfer token if it does not exist:646 await expect(collectionEvm.methods[testCase](receiver, 999999).send({from: sender})).to.be.rejected;647 }));648});649650describe('NFT: Fees', () => {651 let donor: IKeyringPair;652 let alice: IKeyringPair;653 let bob: IKeyringPair;654 let charlie: IKeyringPair;655656 before(async function() {657 await usingEthPlaygrounds(async (helper, privateKey) => {658 donor = await privateKey({filename: __filename});659 [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);660 });661 });662663 itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => {664 const owner = await helper.eth.createAccountWithBalance(donor);665 const spender = helper.eth.createAccount();666667 const collection = await helper.nft.mintCollection(alice, {});668 const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});669670 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);671672 const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, tokenId).send({from: owner}));673 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));674 });675676 itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {677 const owner = await helper.eth.createAccountWithBalance(donor);678 const spender = await helper.eth.createAccountWithBalance(donor);679680 const collection = await helper.nft.mintCollection(alice, {});681 const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});682683 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);684685 await contract.methods.approve(spender, tokenId).send({from: owner});686687 const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, tokenId).send({from: spender}));688 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));689 });690691 itEth('Can perform transferFromCross()', async ({helper}) => {692 const collectionMinter = alice;693 const owner = bob;694 const receiver = charlie;695 const collection = await helper.nft.mintCollection(collectionMinter, {name: 'A', description: 'B', tokenPrefix: 'C'});696697 const spender = await helper.eth.createAccountWithBalance(donor, 100n);698699 const token = await collection.mintToken(collectionMinter, {Substrate: owner.address});700701 const address = helper.ethAddress.fromCollectionId(collection.collectionId);702 const contract = helper.ethNativeContract.collection(address, 'nft');703704 await token.approve(owner, {Ethereum: spender});705706 {707 const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);708 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);709 const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender});710 const event = result.events.Transfer;711 expect(event).to.be.like({712 address: helper.ethAddress.fromCollectionId(collection.collectionId),713 event: 'Transfer',714 returnValues: {715 from: helper.address.substrateToEth(owner.address),716 to: helper.address.substrateToEth(receiver.address),717 tokenId: token.tokenId.toString(),718 },719 });720 }721722 expect(await token.getOwner()).to.be.like({Substrate: receiver.address});723 });724725 itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {726 const owner = await helper.eth.createAccountWithBalance(donor);727 const receiver = helper.eth.createAccount();728729 const collection = await helper.nft.mintCollection(alice, {});730 const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});731732 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);733734 const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, tokenId).send({from: owner}));735 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));736 });737});738739describe('NFT: Substrate calls', () => {740 let donor: IKeyringPair;741 let alice: IKeyringPair;742743 before(async function() {744 await usingEthPlaygrounds(async (helper, privateKey) => {745 donor = await privateKey({filename: __filename});746 [alice] = await helper.arrange.createAccounts([20n], donor);747 });748 });749750 itEth('Events emitted for mint()', async ({helper}) => {751 const collection = await helper.nft.mintCollection(alice, {});752 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);753 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');754755 const events: any = [];756 contract.events.allEvents((_: any, event: any) => {757 events.push(event);758 });759760 const {tokenId} = await collection.mintToken(alice);761 if (events.length == 0) await helper.wait.newBlocks(1);762 const event = events[0];763764 expect(event.event).to.be.equal('Transfer');765 expect(event.address).to.be.equal(collectionAddress);766 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');767 expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(alice.address));768 expect(event.returnValues.tokenId).to.be.equal(tokenId.toString());769 });770771 itEth('Events emitted for burn()', async ({helper}) => {772 const collection = await helper.nft.mintCollection(alice, {});773 const token = await collection.mintToken(alice);774775 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);776 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');777778 const events: any = [];779 contract.events.allEvents((_: any, event: any) => {780 events.push(event);781 });782783 await token.burn(alice);784 if (events.length == 0) await helper.wait.newBlocks(1);785 const event = events[0];786787 expect(event.event).to.be.equal('Transfer');788 expect(event.address).to.be.equal(collectionAddress);789 expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));790 expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000');791 expect(event.returnValues.tokenId).to.be.equal(token.tokenId.toString());792 });793794 itEth('Events emitted for approve()', async ({helper}) => {795 const receiver = helper.eth.createAccount();796797 const collection = await helper.nft.mintCollection(alice, {});798 const token = await collection.mintToken(alice);799800 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);801 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');802803 const events: any = [];804 contract.events.allEvents((_: any, event: any) => {805 events.push(event);806 });807808 await token.approve(alice, {Ethereum: receiver});809 if (events.length == 0) await helper.wait.newBlocks(1);810 const event = events[0];811812 expect(event.event).to.be.equal('Approval');813 expect(event.address).to.be.equal(collectionAddress);814 expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));815 expect(event.returnValues.approved).to.be.equal(receiver);816 expect(event.returnValues.tokenId).to.be.equal(token.tokenId.toString());817 });818819 itEth('Events emitted for transferFrom()', async ({helper}) => {820 const [bob] = await helper.arrange.createAccounts([10n], donor);821 const receiver = helper.eth.createAccount();822823 const collection = await helper.nft.mintCollection(alice, {});824 const token = await collection.mintToken(alice);825 await token.approve(alice, {Substrate: bob.address});826827 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);828 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');829830 const events: any = [];831 contract.events.allEvents((_: any, event: any) => {832 events.push(event);833 });834835 await token.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver});836837 if (events.length == 0) await helper.wait.newBlocks(1);838 const event = events[0];839840 expect(event.address).to.be.equal(collectionAddress);841 expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));842 expect(event.returnValues.to).to.be.equal(receiver);843 expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`);844 });845846 itEth('Events emitted for transfer()', async ({helper}) => {847 const receiver = helper.eth.createAccount();848849 const collection = await helper.nft.mintCollection(alice, {});850 const token = await collection.mintToken(alice);851852 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);853 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');854855 const events: any = [];856 contract.events.allEvents((_: any, event: any) => {857 events.push(event);858 });859860 await token.transfer(alice, {Ethereum: receiver});861862 if (events.length == 0) await helper.wait.newBlocks(1);863 const event = events[0];864865 expect(event.address).to.be.equal(collectionAddress);866 expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));867 expect(event.returnValues.to).to.be.equal(receiver);868 expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`);869 });870});871872describe('Common metadata', () => {873 let donor: IKeyringPair;874 let alice: IKeyringPair;875876 before(async function() {877 await usingEthPlaygrounds(async (helper, privateKey) => {878 donor = await privateKey({filename: __filename});879 [alice] = await helper.arrange.createAccounts([20n], donor);880 });881 });882883 itEth('Returns collection name', async ({helper}) => {884 const caller = await helper.eth.createAccountWithBalance(donor);885 const tokenPropertyPermissions = [{886 key: 'URI',887 permission: {888 mutable: true,889 collectionAdmin: true,890 tokenOwner: false,891 },892 }];893 const collection = await helper.nft.mintCollection(894 alice,895 {896 name: 'oh River',897 tokenPrefix: 'CHANGE',898 properties: [{key: 'ERC721Metadata', value: '1'}],899 tokenPropertyPermissions,900 },901 );902903 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);904 const name = await contract.methods.name().call();905 expect(name).to.equal('oh River');906 });907908 itEth('Returns symbol name', async ({helper}) => {909 const caller = await helper.eth.createAccountWithBalance(donor);910 const tokenPropertyPermissions = [{911 key: 'URI',912 permission: {913 mutable: true,914 collectionAdmin: true,915 tokenOwner: false,916 },917 }];918 const collection = await helper.nft.mintCollection(919 alice,920 {921 name: 'oh River',922 tokenPrefix: 'CHANGE',923 properties: [{key: 'ERC721Metadata', value: '1'}],924 tokenPropertyPermissions,925 },926 );927928 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);929 const symbol = await contract.methods.symbol().call();930 expect(symbol).to.equal('CHANGE');931 });932});933934describe('Negative tests', () => {935 let donor: IKeyringPair;936 let minter: IKeyringPair;937 let alice: IKeyringPair;938939 before(async function() {940 await usingEthPlaygrounds(async (helper, privateKey) => {941 donor = await privateKey({filename: __filename});942 [minter, alice] = await helper.arrange.createAccounts([100n, 100n], donor);943 });944 });945946 itEth('[negative] Cant perform burn without approval', async ({helper}) => {947 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});948949 const owner = await helper.eth.createAccountWithBalance(donor, 100n);950 const spender = await helper.eth.createAccountWithBalance(donor, 100n);951952 const token = await collection.mintToken(minter, {Ethereum: owner});953954 const address = helper.ethAddress.fromCollectionId(collection.collectionId);955 const contract = helper.ethNativeContract.collection(address, 'nft');956957 const ownerCross = helper.ethCrossAccount.fromAddress(owner);958 await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;959960 await contract.methods.setApprovalForAll(spender, true).send({from: owner});961 await contract.methods.setApprovalForAll(spender, false).send({from: owner});962963 await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;964 });965966 itEth('[negative] Cant perform transfer without approval', async ({helper}) => {967 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});968 const receiver = alice;969970 const owner = await helper.eth.createAccountWithBalance(donor, 100n);971 const spender = await helper.eth.createAccountWithBalance(donor, 100n);972973 const token = await collection.mintToken(minter, {Ethereum: owner});974975 const address = helper.ethAddress.fromCollectionId(collection.collectionId);976 const contract = helper.ethNativeContract.collection(address, 'nft');977978 const ownerCross = helper.ethCrossAccount.fromAddress(owner);979 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);980981 await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;982983 await contract.methods.setApprovalForAll(spender, true).send({from: owner});984 await contract.methods.setApprovalForAll(spender, false).send({from: owner});985 986 await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;987 });988});tests/src/eth/reFungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/reFungible.test.ts
+++ b/tests/src/eth/reFungible.test.ts
@@ -17,6 +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';
describe('Refungible: Information getting', () => {
let donor: IKeyringPair;
@@ -135,6 +136,44 @@
expect(await contract.methods.crossOwnerOf(tokenId).call()).to.be.like([receiver, '0']);
expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');
});
+
+ itEth('Can perform mintCross()', async ({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+ const receiverCross = helper.ethCrossAccount.fromKeyringPair(bob);
+ const properties = Array(5).fill(0).map((_, i) => { return {key: `key_${i}`, value: Buffer.from(`value_${i}`)}; });
+ const permissions: ITokenPropertyPermission[] = properties.map(p => { return {key: p.key, permission: {tokenOwner: true,
+ collectionAdmin: true,
+ mutable: true}}; });
+
+
+ const collection = await helper.rft.mintCollection(minter, {
+ tokenPrefix: 'ethp',
+ tokenPropertyPermissions: permissions,
+ });
+ await collection.addAdmin(minter, {Ethereum: caller});
+
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller, true);
+ let expectedTokenId = await contract.methods.nextTokenId().call();
+ let result = await contract.methods.mintCross(receiverCross, []).send();
+ let tokenId = result.events.Transfer.returnValues.tokenId;
+ expect(tokenId).to.be.equal(expectedTokenId);
+
+ 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(helper.address.substrateToEth(bob.address));
+ expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);
+
+ expectedTokenId = await contract.methods.nextTokenId().call();
+ result = await contract.methods.mintCross(receiverCross, properties).send();
+ tokenId = result.events.Transfer.returnValues.tokenId;
+
+ expect(tokenId).to.be.equal(expectedTokenId);
+
+ expect(await contract.methods.properties(tokenId, []).call()).to.be.like(properties
+ .map(p => { return helper.ethProperty.property(p.key, p.value.toString()); }));
+ });
itEth.skip('Can perform mintBulk()', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);