difftreelog
chore add `collectionCreationFee` method to CollectionHelpers
in: master
6 files changed
pallets/unique/src/eth/stubs/CollectionHelpers.rawdiffbeforeafterbothbinary blob — no preview
pallets/unique/src/eth/stubs/CollectionHelpers.soldiffbeforeafterboth--- a/pallets/unique/src/eth/stubs/CollectionHelpers.sol
+++ b/pallets/unique/src/eth/stubs/CollectionHelpers.sol
@@ -23,7 +23,7 @@
}
/// @title Contract, which allows users to operate with collections
-/// @dev the ERC-165 identifier for this interface is 0x88ee8ef1
+/// @dev the ERC-165 identifier for this interface is 0x5ad4f440
contract CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {
/// Create an NFT collection
/// @param name Name of the collection
@@ -105,4 +105,12 @@
dummy;
return false;
}
+
+ /// @dev EVM selector for this function is: 0xd23a7ab1,
+ /// or in textual repr: collectionCreationFee()
+ function collectionCreationFee() public view returns (uint256) {
+ require(false, stub_error);
+ dummy;
+ return 0;
+ }
}
tests/src/eth/api/CollectionHelpers.soldiffbeforeafterboth--- a/tests/src/eth/api/CollectionHelpers.sol
+++ b/tests/src/eth/api/CollectionHelpers.sol
@@ -18,7 +18,7 @@
}
/// @title Contract, which allows users to operate with collections
-/// @dev the ERC-165 identifier for this interface is 0x88ee8ef1
+/// @dev the ERC-165 identifier for this interface is 0x5ad4f440
interface CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {
/// Create an NFT collection
/// @param name Name of the collection
@@ -65,4 +65,8 @@
/// @dev EVM selector for this function is: 0xc3de1494,
/// or in textual repr: isCollectionExist(address)
function isCollectionExist(address collectionAddress) external view returns (bool);
+
+ /// @dev EVM selector for this function is: 0xd23a7ab1,
+ /// or in textual repr: collectionCreationFee()
+ function collectionCreationFee() external view returns (uint256);
}
tests/src/eth/collectionHelpersAbi.jsondiffbeforeafterboth--- a/tests/src/eth/collectionHelpersAbi.json
+++ b/tests/src/eth/collectionHelpersAbi.json
@@ -19,6 +19,13 @@
"type": "event"
},
{
+ "inputs": [],
+ "name": "collectionCreationFee",
+ "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
"inputs": [
{ "internalType": "string", "name": "name", "type": "string" },
{ "internalType": "string", "name": "description", "type": "string" },
tests/src/eth/payable.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 {IKeyringPair} from '@polkadot/types/types';1819import {itEth, expect, usingEthPlaygrounds, EthUniqueHelper} from './util/playgrounds';2021describe('EVM payable contracts', () => {22 let donor: IKeyringPair;2324 before(async function() {25 await usingEthPlaygrounds(async (_, privateKey) => {26 donor = privateKey('//Alice');27 });28 });2930 itEth('Evm contract can receive wei from eth account', async ({helper}) => {31 const deployer = await helper.eth.createAccountWithBalance(donor);32 const contract = await helper.eth.deployCollectorContract(deployer);3334 const web3 = helper.getWeb3();3536 await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: '10000', gas: helper.eth.DEFAULT_GAS});3738 expect(await contract.methods.getCollected().call()).to.be.equal('10000');39 });4041 itEth('Evm contract can receive wei from substrate account', async ({helper}) => {42 const deployer = await helper.eth.createAccountWithBalance(donor);43 const contract = await helper.eth.deployCollectorContract(deployer);44 const [alice] = await helper.arrange.createAccounts([10n], donor);4546 const weiCount = '10000';4748 // Transaction fee/value will be payed from subToEth(sender) evm balance,49 // which is backed by evmToAddress(subToEth(sender)) substrate balance50 await helper.eth.transferBalanceFromSubstrate(alice, helper.address.substrateToEth(alice.address), 5n);515253 await helper.eth.sendEVM(alice, contract.options.address, contract.methods.giveMoney().encodeABI(), weiCount);5455 expect(await contract.methods.getCollected().call()).to.be.equal(weiCount);56 });5758 // We can't handle sending balance to backing storage of evm balance, because evmToAddress operation is irreversible59 itEth('Wei sent directly to backing storage of evm contract balance is unaccounted', async({helper}) => {60 const deployer = await helper.eth.createAccountWithBalance(donor);61 const contract = await helper.eth.deployCollectorContract(deployer);62 const [alice] = await helper.arrange.createAccounts([10n], donor);6364 const weiCount = 10_000n;6566 await helper.eth.transferBalanceFromSubstrate(alice, contract.options.address, weiCount, false);6768 expect(await contract.methods.getUnaccounted().call()).to.be.equal(weiCount.toString());69 });7071 itEth('Balance can be retrieved from evm contract', async({helper, privateKey}) => {72 const FEE_BALANCE = 10n * helper.balance.getOneTokenNominal();73 const CONTRACT_BALANCE = 1n * helper.balance.getOneTokenNominal();7475 const deployer = await helper.eth.createAccountWithBalance(donor);76 const contract = await helper.eth.deployCollectorContract(deployer);77 const [alice] = await helper.arrange.createAccounts([20n], donor);7879 const web3 = helper.getWeb3();8081 await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: CONTRACT_BALANCE.toString(), gas: helper.eth.DEFAULT_GAS});8283 const receiver = privateKey(`//Receiver${Date.now()}`);8485 // First receive balance on eth balance of bob86 {87 const ethReceiver = helper.address.substrateToEth(receiver.address);88 expect(await web3.eth.getBalance(ethReceiver)).to.be.equal('0');89 await contract.methods.withdraw(ethReceiver).send({from: deployer});90 expect(await web3.eth.getBalance(ethReceiver)).to.be.equal(CONTRACT_BALANCE.toString());91 }9293 // Some balance is required to pay fee for evm.withdraw call94 await helper.balance.transferToSubstrate(alice, receiver.address, FEE_BALANCE);95 // await transferBalanceExpectSuccess(api, alice, receiver.address, FEE_BALANCE.toString());9697 // Withdraw balance from eth to substrate98 {99 const initialReceiverBalance = await helper.balance.getSubstrate(receiver.address);100 await helper.executeExtrinsic(receiver, 'api.tx.evm.withdraw', [helper.address.substrateToEth(receiver.address), CONTRACT_BALANCE.toString()], true);101 const finalReceiverBalance = await helper.balance.getSubstrate(receiver.address);102103 expect(finalReceiverBalance > initialReceiverBalance).to.be.true;104 }105 });106});107108describe('EVM transaction fees', () => {109 let donor: IKeyringPair;110111 before(async function() {112 await usingEthPlaygrounds(async (_, privateKey) => {113 donor = privateKey('//Alice');114 });115 });116117 itEth('Fee is withdrawn from the user', async({helper}) => {118 const deployer = await helper.eth.createAccountWithBalance(donor);119 const caller = await helper.eth.createAccountWithBalance(donor);120 const contract = await helper.eth.deployFlipper(deployer);121 122 const initialCallerBalance = await helper.balance.getEthereum(caller);123 await contract.methods.flip().send({from: caller});124 const finalCallerBalance = await helper.balance.getEthereum(caller);125 expect(finalCallerBalance < initialCallerBalance).to.be.true;126 });127128 itEth('Fee for nested calls is withdrawn from the user', async({helper}) => {129 const deployer = await helper.eth.createAccountWithBalance(donor);130 const caller = await helper.eth.createAccountWithBalance(donor);131 const contract = await deployProxyContract(helper, deployer);132 133 const initialCallerBalance = await helper.balance.getEthereum(caller);134 const initialContractBalance = await helper.balance.getEthereum(contract.options.address);135 await contract.methods.flip().send({from: caller});136 const finalCallerBalance = await helper.balance.getEthereum(caller);137 const finalContractBalance = await helper.balance.getEthereum(contract.options.address);138 expect(finalCallerBalance < initialCallerBalance).to.be.true;139 expect(finalContractBalance == initialContractBalance).to.be.true;140 });141 142 itEth('Fee for nested calls to native methods is withdrawn from the user', async({helper}) => {143 const CONTRACT_BALANCE = 2n * helper.balance.getOneTokenNominal();144145 const deployer = await helper.eth.createAccountWithBalance(donor);146 const caller = await helper.eth.createAccountWithBalance(donor);147 const contract = await deployProxyContract(helper, deployer);148149 const collectionAddress = (await contract.methods.createNonfungibleCollection().send({from: caller, value: Number(CONTRACT_BALANCE)})).events.CollectionCreated.returnValues.collection;150 const initialCallerBalance = await helper.balance.getEthereum(caller);151 const initialContractBalance = await helper.balance.getEthereum(contract.options.address);152 await contract.methods.mintNftToken(collectionAddress).send({from: caller});153 const finalCallerBalance = await helper.balance.getEthereum(caller);154 const finalContractBalance = await helper.balance.getEthereum(contract.options.address);155 expect(finalCallerBalance < initialCallerBalance).to.be.true;156 expect(finalContractBalance == initialContractBalance).to.be.true;157 });158 159 itEth('Fee for nested calls to create*Collection methods is withdrawn from the user and from the contract', async({helper}) => {160 const CONTRACT_BALANCE = 2n * helper.balance.getOneTokenNominal();161 const deployer = await helper.eth.createAccountWithBalance(donor);162 const caller = await helper.eth.createAccountWithBalance(donor);163 const contract = await deployProxyContract(helper, deployer);164165 const initialCallerBalance = await helper.balance.getEthereum(caller);166 const initialContractBalance = await helper.balance.getEthereum(contract.options.address);167 await contract.methods.createNonfungibleCollection().send({from: caller, value: Number(CONTRACT_BALANCE)});168 const finalCallerBalance = await helper.balance.getEthereum(caller);169 const finalContractBalance = await helper.balance.getEthereum(contract.options.address);170 expect(finalCallerBalance < initialCallerBalance).to.be.true;171 expect(finalContractBalance == initialContractBalance).to.be.true;172 });173174 async function deployProxyContract(helper: EthUniqueHelper, deployer: string) {175 return await helper.ethContract.deployByCode(176 deployer,177 'ProxyContract',178 `179 // SPDX-License-Identifier: UNLICENSED180 pragma solidity ^0.8.6;181182 import {CollectionHelpers} from "../api/CollectionHelpers.sol";183 import {UniqueNFT} from "../api/UniqueNFT.sol";184185 error Value(uint256 value);186187 contract ProxyContract {188 bool value = false;189 address flipper;190191 event CollectionCreated(address collection);192 event TokenMinted(uint256 tokenId);193194 receive() external payable {}195196 constructor() {197 flipper = address(new Flipper());198 }199200 function flip() public {201 value = !value;202 Flipper(flipper).flip();203 }204205 function createNonfungibleCollection() external payable {206 address collectionHelpers = 0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F;207 address nftCollection = CollectionHelpers(collectionHelpers).createNonfungibleCollection{value: msg.value}("A", "B", "C");208 emit CollectionCreated(nftCollection);209 }210211 function mintNftToken(address collectionAddress) external {212 UniqueNFT collection = UniqueNFT(collectionAddress);213 uint256 tokenId = collection.nextTokenId();214 collection.mint(msg.sender, tokenId);215 emit TokenMinted(tokenId);216 }217218 function getValue() external view returns (bool) {219 return Flipper(flipper).getValue();220 }221 }222223 contract Flipper {224 bool value = false;225 function flip() external {226 value = !value;227 }228 function getValue() external view returns (bool) {229 return value;230 }231 }232 `,233 [234 {235 solPath: 'api/CollectionHelpers.sol',236 fsPath: `${__dirname}/api/CollectionHelpers.sol`,237 },238 {239 solPath: 'api/UniqueNFT.sol',240 fsPath: `${__dirname}/api/UniqueNFT.sol`,241 },242 ],243 );244 }245});1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {IKeyringPair} from '@polkadot/types/types';1819import {itEth, expect, usingEthPlaygrounds, EthUniqueHelper} from './util/playgrounds';2021describe('EVM payable contracts', () => {22 let donor: IKeyringPair;2324 before(async function() {25 await usingEthPlaygrounds(async (_, privateKey) => {26 donor = privateKey('//Alice');27 });28 });2930 itEth('Evm contract can receive wei from eth account', async ({helper}) => {31 const deployer = await helper.eth.createAccountWithBalance(donor);32 const contract = await helper.eth.deployCollectorContract(deployer);3334 const web3 = helper.getWeb3();3536 await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: '10000', gas: helper.eth.DEFAULT_GAS});3738 expect(await contract.methods.getCollected().call()).to.be.equal('10000');39 });4041 itEth('Evm contract can receive wei from substrate account', async ({helper}) => {42 const deployer = await helper.eth.createAccountWithBalance(donor);43 const contract = await helper.eth.deployCollectorContract(deployer);44 const [alice] = await helper.arrange.createAccounts([10n], donor);4546 const weiCount = '10000';4748 // Transaction fee/value will be payed from subToEth(sender) evm balance,49 // which is backed by evmToAddress(subToEth(sender)) substrate balance50 await helper.eth.transferBalanceFromSubstrate(alice, helper.address.substrateToEth(alice.address), 5n);515253 await helper.eth.sendEVM(alice, contract.options.address, contract.methods.giveMoney().encodeABI(), weiCount);5455 expect(await contract.methods.getCollected().call()).to.be.equal(weiCount);56 });5758 // We can't handle sending balance to backing storage of evm balance, because evmToAddress operation is irreversible59 itEth('Wei sent directly to backing storage of evm contract balance is unaccounted', async({helper}) => {60 const deployer = await helper.eth.createAccountWithBalance(donor);61 const contract = await helper.eth.deployCollectorContract(deployer);62 const [alice] = await helper.arrange.createAccounts([10n], donor);6364 const weiCount = 10_000n;6566 await helper.eth.transferBalanceFromSubstrate(alice, contract.options.address, weiCount, false);6768 expect(await contract.methods.getUnaccounted().call()).to.be.equal(weiCount.toString());69 });7071 itEth('Balance can be retrieved from evm contract', async({helper, privateKey}) => {72 const FEE_BALANCE = 10n * helper.balance.getOneTokenNominal();73 const CONTRACT_BALANCE = 1n * helper.balance.getOneTokenNominal();7475 const deployer = await helper.eth.createAccountWithBalance(donor);76 const contract = await helper.eth.deployCollectorContract(deployer);77 const [alice] = await helper.arrange.createAccounts([20n], donor);7879 const web3 = helper.getWeb3();8081 await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: CONTRACT_BALANCE.toString(), gas: helper.eth.DEFAULT_GAS});8283 const receiver = privateKey(`//Receiver${Date.now()}`);8485 // First receive balance on eth balance of bob86 {87 const ethReceiver = helper.address.substrateToEth(receiver.address);88 expect(await web3.eth.getBalance(ethReceiver)).to.be.equal('0');89 await contract.methods.withdraw(ethReceiver).send({from: deployer});90 expect(await web3.eth.getBalance(ethReceiver)).to.be.equal(CONTRACT_BALANCE.toString());91 }9293 // Some balance is required to pay fee for evm.withdraw call94 await helper.balance.transferToSubstrate(alice, receiver.address, FEE_BALANCE);95 // await transferBalanceExpectSuccess(api, alice, receiver.address, FEE_BALANCE.toString());9697 // Withdraw balance from eth to substrate98 {99 const initialReceiverBalance = await helper.balance.getSubstrate(receiver.address);100 await helper.executeExtrinsic(receiver, 'api.tx.evm.withdraw', [helper.address.substrateToEth(receiver.address), CONTRACT_BALANCE.toString()], true);101 const finalReceiverBalance = await helper.balance.getSubstrate(receiver.address);102103 expect(finalReceiverBalance > initialReceiverBalance).to.be.true;104 }105 });106});107108describe('EVM transaction fees', () => {109 let donor: IKeyringPair;110111 before(async function() {112 await usingEthPlaygrounds(async (_, privateKey) => {113 donor = privateKey('//Alice');114 });115 });116117 itEth('Fee is withdrawn from the user', async({helper}) => {118 const deployer = await helper.eth.createAccountWithBalance(donor);119 const caller = await helper.eth.createAccountWithBalance(donor);120 const contract = await helper.eth.deployFlipper(deployer);121 122 const initialCallerBalance = await helper.balance.getEthereum(caller);123 await contract.methods.flip().send({from: caller});124 const finalCallerBalance = await helper.balance.getEthereum(caller);125 expect(finalCallerBalance < initialCallerBalance).to.be.true;126 });127128 itEth('Fee for nested calls is withdrawn from the user', async({helper}) => {129 const deployer = await helper.eth.createAccountWithBalance(donor);130 const caller = await helper.eth.createAccountWithBalance(donor);131 const contract = await deployProxyContract(helper, deployer);132 133 const initialCallerBalance = await helper.balance.getEthereum(caller);134 const initialContractBalance = await helper.balance.getEthereum(contract.options.address);135 await contract.methods.flip().send({from: caller});136 const finalCallerBalance = await helper.balance.getEthereum(caller);137 const finalContractBalance = await helper.balance.getEthereum(contract.options.address);138 expect(finalCallerBalance < initialCallerBalance).to.be.true;139 expect(finalContractBalance == initialContractBalance).to.be.true;140 });141 142 itEth('Fee for nested calls to native methods is withdrawn from the user', async({helper}) => {143 const CONTRACT_BALANCE = 2n * helper.balance.getOneTokenNominal();144145 const deployer = await helper.eth.createAccountWithBalance(donor);146 const caller = await helper.eth.createAccountWithBalance(donor);147 const contract = await deployProxyContract(helper, deployer);148149 const collectionAddress = (await contract.methods.createNonfungibleCollection().send({from: caller, value: Number(CONTRACT_BALANCE)})).events.CollectionCreated.returnValues.collection;150 const initialCallerBalance = await helper.balance.getEthereum(caller);151 const initialContractBalance = await helper.balance.getEthereum(contract.options.address);152 await contract.methods.mintNftToken(collectionAddress).send({from: caller});153 const finalCallerBalance = await helper.balance.getEthereum(caller);154 const finalContractBalance = await helper.balance.getEthereum(contract.options.address);155 expect(finalCallerBalance < initialCallerBalance).to.be.true;156 expect(finalContractBalance == initialContractBalance).to.be.true;157 });158 159 itEth('Fee for nested calls to create*Collection methods is withdrawn from the user and from the contract', async({helper}) => {160 const CONTRACT_BALANCE = 2n * helper.balance.getOneTokenNominal();161 const deployer = await helper.eth.createAccountWithBalance(donor);162 const caller = await helper.eth.createAccountWithBalance(donor);163 const contract = await deployProxyContract(helper, deployer);164165 const initialCallerBalance = await helper.balance.getEthereum(caller);166 const initialContractBalance = await helper.balance.getEthereum(contract.options.address);167 await contract.methods.createNonfungibleCollection().send({from: caller, value: Number(CONTRACT_BALANCE)});168 const finalCallerBalance = await helper.balance.getEthereum(caller);169 const finalContractBalance = await helper.balance.getEthereum(contract.options.address);170 expect(finalCallerBalance < initialCallerBalance).to.be.true;171 expect(finalContractBalance == initialContractBalance).to.be.true;172 });173174 itEth('Get collection creation fee', async({helper}) => {175 const deployer = await helper.eth.createAccountWithBalance(donor);176 expect(await helper.eth.getCollectionCreationFee(deployer)).to.be.equal(String(2n * helper.balance.getOneTokenNominal()));177 });178179 async function deployProxyContract(helper: EthUniqueHelper, deployer: string) {180 return await helper.ethContract.deployByCode(181 deployer,182 'ProxyContract',183 `184 // SPDX-License-Identifier: UNLICENSED185 pragma solidity ^0.8.6;186187 import {CollectionHelpers} from "../api/CollectionHelpers.sol";188 import {UniqueNFT} from "../api/UniqueNFT.sol";189190 error Value(uint256 value);191192 contract ProxyContract {193 bool value = false;194 address flipper;195196 event CollectionCreated(address collection);197 event TokenMinted(uint256 tokenId);198199 receive() external payable {}200201 constructor() {202 flipper = address(new Flipper());203 }204205 function flip() public {206 value = !value;207 Flipper(flipper).flip();208 }209210 function createNonfungibleCollection() external payable {211 address collectionHelpers = 0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F;212 address nftCollection = CollectionHelpers(collectionHelpers).createNonfungibleCollection{value: msg.value}("A", "B", "C");213 emit CollectionCreated(nftCollection);214 }215216 function mintNftToken(address collectionAddress) external {217 UniqueNFT collection = UniqueNFT(collectionAddress);218 uint256 tokenId = collection.nextTokenId();219 collection.mint(msg.sender, tokenId);220 emit TokenMinted(tokenId);221 }222223 function getValue() external view returns (bool) {224 return Flipper(flipper).getValue();225 }226 }227228 contract Flipper {229 bool value = false;230 function flip() external {231 value = !value;232 }233 function getValue() external view returns (bool) {234 return value;235 }236 }237 `,238 [239 {240 solPath: 'api/CollectionHelpers.sol',241 fsPath: `${__dirname}/api/CollectionHelpers.sol`,242 },243 {244 solPath: 'api/UniqueNFT.sol',245 fsPath: `${__dirname}/api/UniqueNFT.sol`,246 },247 ],248 );249 }250});tests/src/eth/util/playgrounds/unique.dev.tsdiffbeforeafterboth--- a/tests/src/eth/util/playgrounds/unique.dev.ts
+++ b/tests/src/eth/util/playgrounds/unique.dev.ts
@@ -152,6 +152,11 @@
async transferBalanceFromSubstrate(donor: IKeyringPair, recepient: string, amount=1000n, inTokens=true) {
return await this.helper.balance.transferToSubstrate(donor, evmToAddress(recepient), amount * (inTokens ? this.helper.balance.getOneTokenNominal() : 1n));
}
+
+ async getCollectionCreationFee(signer: string) {
+ const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer);
+ return await collectionHelper.methods.collectionCreationFee().call();
+ }
async sendEVM(signer: IKeyringPair, contractAddress: string, abi: string, value: string, gasLimit?: number) {
if(!gasLimit) gasLimit = this.DEFAULT_GAS;