From 984c76e4f44ff9d85f5b842f12a4235aa93c5b86 Mon Sep 17 00:00:00 2001 From: Trubnikov Sergey Date: Wed, 01 Jun 2022 09:00:13 +0000 Subject: [PATCH] CORE-386 Implement eth methods --- --- a/pallets/common/src/erc.rs +++ b/pallets/common/src/erc.rs @@ -253,6 +253,42 @@ save(self); Ok(()) } + + fn set_access(&mut self, caller: caller, mode: string) -> Result { + let caller = T::CrossAccountId::from_eth(caller); + self.check_is_owner_or_admin(&caller) + .map_err(dispatch_to_evm::)?; + self.collection.permissions.access = Some(match mode.as_str() { + "Normal" => AccessMode::Normal, + "AllowList" => AccessMode::AllowList, + _ => return Err("Not supported access mode".into()), + }); + save(self); + Ok(()) + } + + fn add_to_allow_list(&self, caller: caller, user: address) -> Result { + let caller = check_is_owner_or_admin(caller, self)?; + let user = T::CrossAccountId::from_eth(user); + >::toggle_allowlist(self, &caller, &user, true) + .map_err(dispatch_to_evm::)?; + Ok(()) + } + + fn remove_from_allow_list(&self, caller: caller, user: address) -> Result { + let caller = check_is_owner_or_admin(caller, self)?; + let user = T::CrossAccountId::from_eth(user); + >::toggle_allowlist(self, &caller, &user, false) + .map_err(dispatch_to_evm::)?; + Ok(()) + } + + fn set_mint_mode(&mut self, caller: caller, mode: bool) -> Result { + check_is_owner_or_admin(caller, self)?; + self.collection.permissions.mint_mode = Some(mode); + save(self); + Ok(()) + } } fn check_is_owner(caller: caller, collection: &CollectionHandle) -> Result { @@ -263,10 +299,7 @@ Ok(()) } -fn check_is_owner_or_admin( - caller: caller, - collection: &CollectionHandle, -) -> Result { +fn check_is_owner_or_admin(caller: caller, collection: &CollectionHandle) -> Result { let caller = T::CrossAccountId::from_eth(caller); collection .check_is_owner_or_admin(&caller) @@ -274,11 +307,7 @@ Ok(caller) } -fn save(collection: &CollectionHandle) -> Result { - // TODO possibly delete for the lack of transaction - collection - .check_is_internal() - .map_err(dispatch_to_evm::)?; +fn save(collection: &CollectionHandle) { >::insert(collection.id, collection.collection.clone()); Ok(()) } --- a/pallets/nonfungible/src/stubs/UniqueNFT.sol +++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol @@ -51,105 +51,6 @@ event MintingFinished(); } -// Selector: 3a54513b -contract Collection is Dummy, ERC165 { - // Selector: setCollectionProperty(string,bytes) 2f073f66 - function setCollectionProperty(string memory key, bytes memory value) - public - { - require(false, stub_error); - key; - value; - dummy = 0; - } - - // Selector: deleteCollectionProperty(string) 7b7debce - function deleteCollectionProperty(string memory key) public { - require(false, stub_error); - key; - dummy = 0; - } - - // Throws error if key not found - // - // Selector: collectionProperty(string) cf24fd6d - function collectionProperty(string memory key) - public - view - returns (bytes memory) - { - require(false, stub_error); - key; - dummy; - return hex""; - } - - // Selector: ethSetSponsor(address) 8f9af356 - function ethSetSponsor(address sponsor) public { - require(false, stub_error); - sponsor; - dummy = 0; - } - - // Selector: ethConfirmSponsorship() a8580d1a - function ethConfirmSponsorship() public { - require(false, stub_error); - dummy = 0; - } - - // Selector: setLimit(string,uint32) 68db30ca - function setLimit(string memory limit, uint32 value) public { - require(false, stub_error); - limit; - value; - dummy = 0; - } - - // Selector: setLimit(string,bool) ea67e4c2 - function setLimit(string memory limit, bool value) public { - require(false, stub_error); - limit; - value; - dummy = 0; - } - - // Selector: contractAddress() f6b4dfb4 - function contractAddress() public view returns (address) { - require(false, stub_error); - dummy; - return 0x0000000000000000000000000000000000000000; - } - - // Selector: addAdmin(address) 70480275 - function addAdmin(address newAdmin) public view { - require(false, stub_error); - newAdmin; - dummy; - } - - // Selector: removeAdmin(address) 1785f53c - function removeAdmin(address admin) public view { - require(false, stub_error); - admin; - dummy; - } - - // Selector: setNesting(bool) e8fc50dd - function setNesting(bool enable) public { - require(false, stub_error); - enable; - dummy = 0; - } - - // Selector: setNesting(bool,address[]) 7df12a9a - function setNesting(bool enable, address[] memory collections) public { - require(false, stub_error); - enable; - collections; - dummy = 0; - } -} - // Selector: 41369377 contract TokenProperties is Dummy, ERC165 { // Selector: setTokenPropertyPermission(string,bool,bool,bool) 222d97fa @@ -479,6 +380,133 @@ } } +// Selector: f56cd7fa +contract Collection is Dummy, ERC165 { + // Selector: setCollectionProperty(string,bytes) 2f073f66 + function setCollectionProperty(string memory key, bytes memory value) + public + { + require(false, stub_error); + key; + value; + dummy = 0; + } + + // Selector: deleteCollectionProperty(string) 7b7debce + function deleteCollectionProperty(string memory key) public { + require(false, stub_error); + key; + dummy = 0; + } + + // Throws error if key not found + // + // Selector: collectionProperty(string) cf24fd6d + function collectionProperty(string memory key) + public + view + returns (bytes memory) + { + require(false, stub_error); + key; + dummy; + return hex""; + } + + // Selector: ethSetSponsor(address) 8f9af356 + function ethSetSponsor(address sponsor) public { + require(false, stub_error); + sponsor; + dummy = 0; + } + + // Selector: ethConfirmSponsorship() a8580d1a + function ethConfirmSponsorship() public { + require(false, stub_error); + dummy = 0; + } + + // Selector: setLimit(string,uint32) 68db30ca + function setLimit(string memory limit, uint32 value) public { + require(false, stub_error); + limit; + value; + dummy = 0; + } + + // Selector: setLimit(string,bool) ea67e4c2 + function setLimit(string memory limit, bool value) public { + require(false, stub_error); + limit; + value; + dummy = 0; + } + + // Selector: contractAddress() f6b4dfb4 + function contractAddress() public view returns (address) { + require(false, stub_error); + dummy; + return 0x0000000000000000000000000000000000000000; + } + + // Selector: addAdmin(address) 70480275 + function addAdmin(address newAdmin) public view { + require(false, stub_error); + newAdmin; + dummy; + } + + // Selector: removeAdmin(address) 1785f53c + function removeAdmin(address admin) public view { + require(false, stub_error); + admin; + dummy; + } + + // Selector: setNesting(bool) e8fc50dd + function setNesting(bool enable) public { + require(false, stub_error); + enable; + dummy = 0; + } + + // Selector: setNesting(bool,address[]) 7df12a9a + function setNesting(bool enable, address[] memory collections) public { + require(false, stub_error); + enable; + collections; + dummy = 0; + } + + // Selector: setAccess(string) 488f56aa + function setAccess(string memory mode) public { + require(false, stub_error); + mode; + dummy = 0; + } + + // Selector: addToAllowList(address) 31f59102 + function addToAllowList(address user) public view { + require(false, stub_error); + user; + dummy; + } + + // Selector: removeFromAllowList(address) eba8dabc + function removeFromAllowList(address user) public view { + require(false, stub_error); + user; + dummy; + } + + // Selector: setMintMode(bool) 5dea9bd5 + function setMintMode(bool mode) public { + require(false, stub_error); + mode; + dummy = 0; + } +} + contract UniqueNFT is Dummy, ERC165, --- a/tests/src/eth/api/UniqueNFT.sol +++ b/tests/src/eth/api/UniqueNFT.sol @@ -42,51 +42,6 @@ event MintingFinished(); } -// Selector: 3a54513b -interface Collection is Dummy, ERC165 { - // Selector: setCollectionProperty(string,bytes) 2f073f66 - function setCollectionProperty(string memory key, bytes memory value) - external; - - // Selector: deleteCollectionProperty(string) 7b7debce - function deleteCollectionProperty(string memory key) external; - - // Throws error if key not found - // - // Selector: collectionProperty(string) cf24fd6d - function collectionProperty(string memory key) - external - view - returns (bytes memory); - - // Selector: ethSetSponsor(address) 8f9af356 - function ethSetSponsor(address sponsor) external; - - // Selector: ethConfirmSponsorship() a8580d1a - function ethConfirmSponsorship() external; - - // Selector: setLimit(string,uint32) 68db30ca - function setLimit(string memory limit, uint32 value) external; - - // Selector: setLimit(string,bool) ea67e4c2 - function setLimit(string memory limit, bool value) external; - - // Selector: contractAddress() f6b4dfb4 - function contractAddress() external view returns (address); - - // Selector: addAdmin(address) 70480275 - function addAdmin(address newAdmin) external view; - - // Selector: removeAdmin(address) 1785f53c - function removeAdmin(address admin) external view; - - // Selector: setNesting(bool) e8fc50dd - function setNesting(bool enable) external; - - // Selector: setNesting(bool,address[]) 7df12a9a - function setNesting(bool enable, address[] memory collections) external; -} - // Selector: 41369377 interface TokenProperties is Dummy, ERC165 { // Selector: setTokenPropertyPermission(string,bool,bool,bool) 222d97fa @@ -258,6 +213,63 @@ returns (bool); } +// Selector: f56cd7fa +interface Collection is Dummy, ERC165 { + // Selector: setCollectionProperty(string,bytes) 2f073f66 + function setCollectionProperty(string memory key, bytes memory value) + external; + + // Selector: deleteCollectionProperty(string) 7b7debce + function deleteCollectionProperty(string memory key) external; + + // Throws error if key not found + // + // Selector: collectionProperty(string) cf24fd6d + function collectionProperty(string memory key) + external + view + returns (bytes memory); + + // Selector: ethSetSponsor(address) 8f9af356 + function ethSetSponsor(address sponsor) external; + + // Selector: ethConfirmSponsorship() a8580d1a + function ethConfirmSponsorship() external; + + // Selector: setLimit(string,uint32) 68db30ca + function setLimit(string memory limit, uint32 value) external; + + // Selector: setLimit(string,bool) ea67e4c2 + function setLimit(string memory limit, bool value) external; + + // Selector: contractAddress() f6b4dfb4 + function contractAddress() external view returns (address); + + // Selector: addAdmin(address) 70480275 + function addAdmin(address newAdmin) external view; + + // Selector: removeAdmin(address) 1785f53c + function removeAdmin(address admin) external view; + + // Selector: setNesting(bool) e8fc50dd + function setNesting(bool enable) external; + + // Selector: setNesting(bool,address[]) 7df12a9a + function setNesting(bool enable, address[] memory collections) external; + + // Selector: setAccess(string) 488f56aa + function setAccess(string memory mode) external; + + // Selector: addToAllowList(address) 31f59102 + function addToAllowList(address user) external view; + + // Selector: removeFromAllowList(address) eba8dabc + function removeFromAllowList(address user) external view; + + // Selector: setMintMode(bool) 5dea9bd5 + function setMintMode(bool mode) external; +} + interface UniqueNFT is Dummy, ERC165, --- a/tests/src/eth/contractSponsoring.test.ts +++ b/tests/src/eth/contractSponsoring.test.ts @@ -221,6 +221,7 @@ expect(await helpers.methods.getSponsoringRateLimit(flipper.options.address).call()).to.be.equals('7200'); }); + //TODO: CORE-302 add eth methods itWeb3('Sponsoring collection from evm address via access list', async ({api, web3}) => { const owner = await createEthAccountWithBalance(api, web3); const collectionHelpers = evmCollectionHelpers(web3, owner); @@ -228,65 +229,60 @@ const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); const sponsor = await createEthAccountWithBalance(api, web3); const collectionEvm = evmCollection(web3, owner, collectionIdAddress); - result = await collectionEvm.methods.setCollectionSponsor(sponsor).send({from: owner}); + result = await collectionEvm.methods.ethSetSponsor(sponsor).send({from: owner}); let collectionSub = (await getDetailedCollectionInfo(api, collectionId))!; expect(collectionSub.sponsorship.isUnconfirmed).to.be.true; expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor)); await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('Caller is not set as sponsor'); - await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); + await collectionEvm.methods.ethConfirmSponsorship().send({from: sponsor}); collectionSub = (await getDetailedCollectionInfo(api, collectionId))!; expect(collectionSub.sponsorship.isConfirmed).to.be.true; expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor)); const user = createEthAccount(web3); - let nextTokenId = await collectionEvm.methods.nextTokenId().call(); + const nextTokenId = await collectionEvm.methods.nextTokenId().call(); expect(nextTokenId).to.be.equal('1'); const oldPermissions = (await getDetailedCollectionInfo(api, collectionId))!.permissions.toHuman(); expect(oldPermissions.mintMode).to.be.false; expect(oldPermissions.access).to.be.equal('Normal'); - //TODO: change value, when enum generated - await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner}); - await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner}); - await collectionEvm.methods.setCollectionMintMode(true).send({from: owner}); + await collectionEvm.methods.setAccess('AllowList').send({from: owner}); + await collectionEvm.methods.addToAllowList(user).send({from: owner}); + await collectionEvm.methods.setMintMode(true).send({from: owner}); const newPermissions = (await getDetailedCollectionInfo(api, collectionId))!.permissions.toHuman(); expect(newPermissions.mintMode).to.be.true; expect(newPermissions.access).to.be.equal('AllowList'); - const ownerBalanceBefore = await ethBalanceViaSub(api, owner); - const sponsorBalanceBefore = await ethBalanceViaSub(api, sponsor); + // const [alicesBalanceBefore] = await getBalance(api, [alicesPublicKey]); - nextTokenId = await collectionEvm.methods.nextTokenId().call({from: user}); - expect(nextTokenId).to.be.equal('1'); - result = await collectionEvm.methods.mintWithTokenURI( - user, - nextTokenId, - 'Test URI', - ).send({from: user}); - const events = normalizeEvents(result.events); - events[0].address = events[0].address.toLocaleLowerCase(); + { + const nextTokenId = await collectionEvm.methods.nextTokenId().call(); + expect(nextTokenId).to.be.equal('1'); + const result = await collectionEvm.methods.mintWithTokenURI( + user, + nextTokenId, + 'Test URI', + ).call({from: user}); + console.log(result); + const events = normalizeEvents(result.events); - expect(events).to.be.deep.equal([ - { - address: collectionIdAddress.toLocaleLowerCase(), - event: 'Transfer', - args: { - from: '0x0000000000000000000000000000000000000000', - to: user, - tokenId: nextTokenId, + expect(events).to.be.deep.equal([ + { + collectionIdAddress, + event: 'Transfer', + args: { + from: '0x0000000000000000000000000000000000000000', + to: user, + tokenId: nextTokenId, + }, }, - }, - ]); - - expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI'); + ]); - const ownerBalanceAfter = await ethBalanceViaSub(api, owner); - expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore); - const sponsorBalanceAfter = await ethBalanceViaSub(api, sponsor); - expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true; + expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI'); + } }); itWeb3('Check that transaction via EVM spend money from sponsor address', async ({api, web3}) => { @@ -296,22 +292,23 @@ const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); const sponsor = await createEthAccountWithBalance(api, web3); const collectionEvm = evmCollection(web3, owner, collectionIdAddress); - result = await collectionEvm.methods.setCollectionSponsor(sponsor).send(); + result = await collectionEvm.methods.ethSetSponsor(sponsor).send(); let collectionSub = (await getDetailedCollectionInfo(api, collectionId))!; expect(collectionSub.sponsorship.isUnconfirmed).to.be.true; expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor)); - await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('Caller is not set as sponsor'); + await expect(collectionEvm.methods.ethConfirmSponsorship().call()).to.be.rejectedWith('Caller is not set as sponsor'); const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress); - await sponsorCollection.methods.confirmCollectionSponsorship().send(); + await sponsorCollection.methods.ethConfirmSponsorship().send(); collectionSub = (await getDetailedCollectionInfo(api, collectionId))!; expect(collectionSub.sponsorship.isConfirmed).to.be.true; expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor)); const user = createEthAccount(web3); - await collectionEvm.methods.addCollectionAdmin(user).send(); + await collectionEvm.methods.addAdmin(user).send(); const ownerBalanceBefore = await ethBalanceViaSub(api, owner); const sponsorBalanceBefore = await ethBalanceViaSub(api, sponsor); + const userCollectionEvm = evmCollection(web3, user, collectionIdAddress); const nextTokenId = await userCollectionEvm.methods.nextTokenId().call(); --- a/tests/src/eth/nonFungibleAbi.json +++ b/tests/src/eth/nonFungibleAbi.json @@ -91,6 +91,15 @@ }, { "inputs": [ + { "internalType": "address", "name": "user", "type": "address" } + ], + "name": "addToAllowList", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ { "internalType": "address", "name": "approved", "type": "address" }, { "internalType": "uint256", "name": "tokenId", "type": "uint256" } ], @@ -291,6 +300,15 @@ }, { "inputs": [ + { "internalType": "address", "name": "user", "type": "address" } + ], + "name": "removeFromAllowList", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ { "internalType": "address", "name": "from", "type": "address" }, { "internalType": "address", "name": "to", "type": "address" }, { "internalType": "uint256", "name": "tokenId", "type": "uint256" } @@ -313,6 +331,13 @@ "type": "function" }, { + "inputs": [{ "internalType": "string", "name": "mode", "type": "string" }], + "name": "setAccess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { "inputs": [ { "internalType": "address", "name": "operator", "type": "address" }, { "internalType": "bool", "name": "approved", "type": "bool" } @@ -397,6 +422,13 @@ "type": "function" }, { + "inputs": [{ "internalType": "bool", "name": "mode", "type": "bool" }], + "name": "setMintMode", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { "inputs": [ { "internalType": "bool", "name": "enable", "type": "bool" }, { -- gitstuff