From d7a043d9818f5f0e8e217c21b050022f0d4e1c14 Mon Sep 17 00:00:00 2001 From: Trubnikov Sergey Date: Wed, 20 Apr 2022 15:05:54 +0000 Subject: [PATCH] CORE-302 Implement setSponsor method. --- --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ TESTS_API=./tests/src/eth/api/ .PHONY: regenerate_solidity -regenerate_solidity: UniqueFungible.sol UniqueNFT.sol ContractHelpers.sol +regenerate_solidity: UniqueFungible.sol UniqueNFT.sol ContractHelpers.sol Collection.sol UniqueFungible.sol: PACKAGE=pallet-fungible NAME=erc::gen_iface OUTPUT=$(TESTS_API)/$@ ./.maintain/scripts/generate_sol.sh @@ -36,8 +36,8 @@ PACKAGE=pallet-evm-contract-helpers NAME=eth::contract_helpers_impl OUTPUT=$(CONTRACT_HELPERS_STUBS)/$@ ./.maintain/scripts/generate_sol.sh Collection.sol: - PACKAGE=pallet-evm-collection NAME=eth::contract_helpers_iface OUTPUT=$(TESTS_API)/$@ ./.maintain/scripts/generate_sol.sh - PACKAGE=pallet-evm-collection NAME=eth::contract_helpers_impl OUTPUT=$(COLLECTION_STUBS)/$@ ./.maintain/scripts/generate_sol.sh + PACKAGE=pallet-evm-collection NAME=eth::collection_iface OUTPUT=$(TESTS_API)/$@ ./.maintain/scripts/generate_sol.sh + PACKAGE=pallet-evm-collection NAME=eth::collection_impl OUTPUT=$(COLLECTION_STUBS)/$@ ./.maintain/scripts/generate_sol.sh UniqueFungible: UniqueFungible.sol INPUT=$(FUNGIBLE_EVM_STUBS)/$< OUTPUT=$(FUNGIBLE_EVM_STUBS)/UniqueFungible.raw ./.maintain/scripts/compile_stub.sh --- a/pallets/common/src/lib.rs +++ b/pallets/common/src/lib.rs @@ -114,6 +114,15 @@ recorder: SubstrateRecorder::new(gas_limit), }) } + + pub fn new_with_recorder(id: CollectionId, recorder: Rc>) -> Option { + >::get(id).map(|collection| Self { + id, + collection, + recorder, + }) + } + pub fn new(id: CollectionId) -> Option { Self::new_with_gas_limit(id, u64::MAX) } @@ -140,6 +149,10 @@ >::insert(self.id, self.collection); Ok(()) } + + pub fn set_sponsor(&mut self, sponsor: T::AccountId) { + self.collection.sponsorship = SponsorshipState::Unconfirmed(sponsor); + } } impl Deref for CollectionHandle { type Target = Collection; --- a/pallets/evm-collection/src/eth.rs +++ b/pallets/evm-collection/src/eth.rs @@ -17,7 +17,7 @@ use core::marker::PhantomData; use evm_coder::{abi::AbiWriter, execution::*, generate_stubgen, solidity_interface, types::*, ToLog}; use ethereum as _; -use pallet_common::CollectionById; +use pallet_common::{CollectionById, CollectionHandle}; use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder}; use pallet_evm::{ ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure, @@ -26,7 +26,7 @@ use sp_core::H160; use up_data_structs::{ CreateCollectionData, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH, - MAX_COLLECTION_NAME_LENGTH, + MAX_COLLECTION_NAME_LENGTH, SponsorshipState, }; use crate::{Config, Pallet}; use frame_support::traits::Get; @@ -57,6 +57,7 @@ #[solidity_interface(name = "Collection")] impl EvmCollection { + fn create_721_collection( &self, caller: caller, @@ -102,15 +103,27 @@ Ok(address) } - // fn set_sponsor(collection_id: address, sponsor: address) -> Result { - // let collection_id = - // pallet_common::eth::map_eth_to_id(&collection_id).ok_or(Error::Revert("".into()))?; - // let mut collection = >::get(collection_id).ok_or(Error::Revert("".into()))?; - // let sponsor = T::CrossAccountId::from_eth(sponsor); - // collection.sponsorship = SponsorshipState::Unconfirmed(sponsor.as_sub().clone()); - // >::insert(collection_id, collection); - // Ok(()) - // } + fn set_sponsor( + &self, + caller: caller, + contract_address: address, + sponsor: address, + ) -> Result { + let collection_id = + pallet_common::eth::map_eth_to_id(&contract_address).ok_or(Error::Revert("".into()))?; + let mut collection = + pallet_common::CollectionHandle::new_with_recorder(collection_id, self.0.clone()) + .ok_or(Error::Revert("".into()))?; + + let caller = T::CrossAccountId::from_eth(caller); + collection.check_is_owner(&caller).map_err(|e| Error::Revert(format!("{:?}", e)))?; + + let sponsor = T::CrossAccountId::from_eth(sponsor); + collection.set_sponsor(sponsor.as_sub().clone()); + collection + .save() + .map_err(|e| Error::Revert(format!("{:?}", e))) + } // fn set_offchain_shema(shema: string) -> Result { // Ok(()) --- a/pallets/evm-collection/src/stubs/Collection.sol +++ b/pallets/evm-collection/src/stubs/Collection.sol @@ -21,130 +21,8 @@ } } -// Selector: ee5467a8 +// Selector: 6503bbc2 contract Collection is Dummy, ERC165 { - // Selector: contractOwner(address) 5152b14c - function contractOwner(address contractAddress) - public - view - returns (address) - { - require(false, stub_error); - contractAddress; - dummy; - return 0x0000000000000000000000000000000000000000; - } - - // Selector: sponsoringEnabled(address) 6027dc61 - function sponsoringEnabled(address contractAddress) - public - view - returns (bool) - { - require(false, stub_error); - contractAddress; - dummy; - return false; - } - - // Deprecated - // - // Selector: toggleSponsoring(address,bool) fcac6d86 - function toggleSponsoring(address contractAddress, bool enabled) public { - require(false, stub_error); - contractAddress; - enabled; - dummy = 0; - } - - // Selector: setSponsoringMode(address,uint8) fde8a560 - function setSponsoringMode(address contractAddress, uint8 mode) public { - require(false, stub_error); - contractAddress; - mode; - dummy = 0; - } - - // Selector: sponsoringMode(address) b70c7267 - function sponsoringMode(address contractAddress) - public - view - returns (uint8) - { - require(false, stub_error); - contractAddress; - dummy; - return 0; - } - - // Selector: setSponsoringRateLimit(address,uint32) 77b6c908 - function setSponsoringRateLimit(address contractAddress, uint32 rateLimit) - public - { - require(false, stub_error); - contractAddress; - rateLimit; - dummy = 0; - } - - // Selector: getSponsoringRateLimit(address) 610cfabd - function getSponsoringRateLimit(address contractAddress) - public - view - returns (uint32) - { - require(false, stub_error); - contractAddress; - dummy; - return 0; - } - - // Selector: allowed(address,address) 5c658165 - function allowed(address contractAddress, address user) - public - view - returns (bool) - { - require(false, stub_error); - contractAddress; - user; - dummy; - return false; - } - - // Selector: allowlistEnabled(address) c772ef6c - function allowlistEnabled(address contractAddress) - public - view - returns (bool) - { - require(false, stub_error); - contractAddress; - dummy; - return false; - } - - // Selector: toggleAllowlist(address,bool) 36de20f5 - function toggleAllowlist(address contractAddress, bool enabled) public { - require(false, stub_error); - contractAddress; - enabled; - dummy = 0; - } - - // Selector: toggleAllowed(address,address,bool) 4706cc1c - function toggleAllowed( - address contractAddress, - address user, - bool allowed - ) public { - require(false, stub_error); - contractAddress; - user; - allowed; - dummy = 0; - } - // Selector: create721Collection(string,string,string) 951c0151 function create721Collection( string memory name, @@ -158,4 +36,12 @@ dummy; return 0x0000000000000000000000000000000000000000; } + + // Selector: setSponsor(address,address) f01fba93 + function setSponsor(address contractAddress, address sponsor) public view { + require(false, stub_error); + contractAddress; + sponsor; + dummy; + } } --- a/pallets/unique/src/lib.rs +++ b/pallets/unique/src/lib.rs @@ -520,7 +520,7 @@ let mut target_collection = >::try_get(collection_id)?; target_collection.check_is_owner(&sender)?; - target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor.clone()); + target_collection.set_sponsor(new_sponsor.clone()); >::deposit_event(Event::::CollectionSponsorSet( collection_id, --- a/tests/src/eth/api/Collection.sol +++ b/tests/src/eth/api/Collection.sol @@ -12,70 +12,15 @@ function supportsInterface(bytes4 interfaceID) external view returns (bool); } -// Selector: ee5467a8 +// Selector: 6503bbc2 interface Collection is Dummy, ERC165 { - // Selector: contractOwner(address) 5152b14c - function contractOwner(address contractAddress) - external - view - returns (address); - - // Selector: sponsoringEnabled(address) 6027dc61 - function sponsoringEnabled(address contractAddress) - external - view - returns (bool); - - // Deprecated - // - // Selector: toggleSponsoring(address,bool) fcac6d86 - function toggleSponsoring(address contractAddress, bool enabled) external; - - // Selector: setSponsoringMode(address,uint8) fde8a560 - function setSponsoringMode(address contractAddress, uint8 mode) external; - - // Selector: sponsoringMode(address) b70c7267 - function sponsoringMode(address contractAddress) - external - view - returns (uint8); - - // Selector: setSponsoringRateLimit(address,uint32) 77b6c908 - function setSponsoringRateLimit(address contractAddress, uint32 rateLimit) - external; - - // Selector: getSponsoringRateLimit(address) 610cfabd - function getSponsoringRateLimit(address contractAddress) - external - view - returns (uint32); - - // Selector: allowed(address,address) 5c658165 - function allowed(address contractAddress, address user) - external - view - returns (bool); - - // Selector: allowlistEnabled(address) c772ef6c - function allowlistEnabled(address contractAddress) - external - view - returns (bool); - - // Selector: toggleAllowlist(address,bool) 36de20f5 - function toggleAllowlist(address contractAddress, bool enabled) external; - - // Selector: toggleAllowed(address,address,bool) 4706cc1c - function toggleAllowed( - address contractAddress, - address user, - bool allowed - ) external; - // Selector: create721Collection(string,string,string) 951c0151 function create721Collection( string memory name, string memory description, string memory tokenPrefix ) external view returns (address); + + // Selector: setSponsor(address,address) f01fba93 + function setSponsor(address contractAddress, address sponsor) external view; } --- a/tests/src/eth/collectionAbi.json +++ b/tests/src/eth/collectionAbi.json @@ -1,65 +1,12 @@ [ { "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - }, - { "internalType": "address", "name": "user", "type": "address" } - ], - "name": "allowed", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - } - ], - "name": "allowlistEnabled", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - } - ], - "name": "contractOwner", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ { "internalType": "string", "name": "name", "type": "string" }, { "internalType": "string", "name": "description", "type": "string" }, { "internalType": "string", "name": "tokenPrefix", "type": "string" } ], "name": "create721Collection", "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - } - ], - "name": "getSponsoringRateLimit", - "outputs": [{ "internalType": "uint32", "name": "", "type": "uint32" }], "stateMutability": "view", "type": "function" }, @@ -70,103 +17,20 @@ "name": "contractAddress", "type": "address" }, - { "internalType": "uint8", "name": "mode", "type": "uint8" } - ], - "name": "setSponsoringMode", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - }, - { "internalType": "uint32", "name": "rateLimit", "type": "uint32" } + { "internalType": "address", "name": "sponsor", "type": "address" } ], - "name": "setSponsoringRateLimit", + "name": "setSponsor", "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - } - ], - "name": "sponsoringEnabled", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "view", "type": "function" }, { "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - } - ], - "name": "sponsoringMode", - "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" } ], "name": "supportsInterface", "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - }, - { "internalType": "address", "name": "user", "type": "address" }, - { "internalType": "bool", "name": "allowed", "type": "bool" } - ], - "name": "toggleAllowed", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - }, - { "internalType": "bool", "name": "enabled", "type": "bool" } - ], - "name": "toggleAllowlist", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - }, - { "internalType": "bool", "name": "enabled", "type": "bool" } - ], - "name": "toggleSponsoring", - "outputs": [], - "stateMutability": "nonpayable", "type": "function" } ] --- a/tests/src/eth/createCollection.test.ts +++ b/tests/src/eth/createCollection.test.ts @@ -14,32 +14,53 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . +import {ApiPromise} from '@polkadot/api'; +import {evmToAddress} from '@polkadot/util-crypto'; import {expect} from 'chai'; import {getCreatedCollectionCount, getDetailedCollectionInfo} from '../util/helpers'; -import {collectionHelper, collectionIdFromAddress, contractHelpers, createEthAccountWithBalance, itWeb3} from './util/helpers'; +import {collectionHelper, collectionIdFromAddress, createEthAccountWithBalance, itWeb3, normalizeAddress} from './util/helpers'; +async function getCollectionAddressFromResult(api: ApiPromise, result: any) { + const collectionIdAddress = normalizeAddress(result.events[0].raw.topics[2]); + const collectionId = collectionIdFromAddress(collectionIdAddress); + const collection = (await getDetailedCollectionInfo(api, collectionId))!; + return {collectionIdAddress, collectionId, collection}; +} + describe('Create collection from EVM', () => { itWeb3('Create collection', async ({api, web3}) => { const owner = await createEthAccountWithBalance(api, web3); - const helpers = collectionHelper(web3, owner); + const helper = collectionHelper(web3, owner); const collectionName = 'CollectionEVM'; const description = 'Some description'; const tokenPrefix = 'token prefix'; const collectionCountBefore = await getCreatedCollectionCount(api); - const result = await helpers.methods + const result = await helper.methods .create721Collection(collectionName, description, tokenPrefix) .send(); const collectionCountAfter = await getCreatedCollectionCount(api); - const collectionId = collectionIdFromAddress(result.events[0].raw.topics[2]); + const {collectionId, collection} = await getCollectionAddressFromResult(api, result); expect(collectionCountAfter - collectionCountBefore).to.be.eq(1); expect(collectionId).to.be.eq(collectionCountAfter); - - const collection = (await getDetailedCollectionInfo(api, collectionId))!; expect(collection.name.map(v => String.fromCharCode(v.toNumber())).join('')).to.be.eq(collectionName); expect(collection.description.map(v => String.fromCharCode(v.toNumber())).join('')).to.be.eq(description); expect(collection.tokenPrefix.toHuman()).to.be.eq(tokenPrefix); expect(collection.schemaVersion.type).to.be.eq('ImageURL'); }); + + itWeb3('Set sponsorship', async ({api, web3}) => { + const owner = await createEthAccountWithBalance(api, web3); + const helper = collectionHelper(web3, owner); + let result = await helper.methods.create721Collection('Sponsor collection', '1', '1').send(); + const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); + const sponsor = await createEthAccountWithBalance(api, web3); + result = await helper.methods.setSponsor(collectionIdAddress, sponsor).send(); + const collection = (await getDetailedCollectionInfo(api, collectionId))!; + expect(collection.sponsorship.isUnconfirmed).to.be.true; + expect(collection.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor)); + }); + + }); \ No newline at end of file --- a/tests/src/eth/util/helpers.ts +++ b/tests/src/eth/util/helpers.ts @@ -74,7 +74,15 @@ return Web3.utils.toChecksumAddress('0x' + buf.toString('hex')); } export function collectionIdFromAddress(address: string): number { - return Number('0x' + address.substring(address.length - 8)); + if (!address.startsWith('0x')) + throw 'address not starts with "0x"'; + if (address.length > 42) + throw 'address length is more than 20 bytes'; + return Number('0x' + address.substring(address.length - 8)); +} + +export function normalizeAddress(address: string): string { + return '0x' + address.substring(address.length - 40); } export function tokenIdToAddress(collection: number, token: number): string { -- gitstuff