--- a/Makefile +++ b/Makefile @@ -15,7 +15,6 @@ NONFUNGIBLE_EVM_STUBS=./pallets/nonfungible/src/stubs NONFUNGIBLE_EVM_ABI=./tests/src/eth/nonFungibleAbi.json -REFUNGIBLE_EVM_STUBS=./pallets/refungible/src/stubs RENFUNGIBLE_EVM_ABI=./tests/src/eth/reFungibleAbi.json RENFUNGIBLE_TOKEN_EVM_ABI=./tests/src/eth/reFungibleTokenAbi.json @@ -28,7 +27,7 @@ TESTS_API=./tests/src/eth/api/ .PHONY: regenerate_solidity -regenerate_solidity: UniqueFungible.sol UniqueNFT.sol UniqueRefungibleToken.sol ContractHelpers.sol CollectionHelpers.sol +regenerate_solidity: UniqueFungible.sol UniqueNFT.sol UniqueRefungible.sol UniqueRefungibleToken.sol ContractHelpers.sol CollectionHelpers.sol UniqueFungible.sol: PACKAGE=pallet-fungible NAME=erc::gen_iface OUTPUT=$(TESTS_API)/$@ ./.maintain/scripts/generate_sol.sh @@ -38,9 +37,9 @@ PACKAGE=pallet-nonfungible NAME=erc::gen_iface OUTPUT=$(TESTS_API)/$@ ./.maintain/scripts/generate_sol.sh PACKAGE=pallet-nonfungible NAME=erc::gen_impl OUTPUT=$(NONFUNGIBLE_EVM_STUBS)/$@ ./.maintain/scripts/generate_sol.sh -UniqueRefungibleToken.sol: - PACKAGE=pallet-refungible NAME=erc_token::gen_iface OUTPUT=$(TESTS_API)/$@ ./.maintain/scripts/generate_sol.sh - PACKAGE=pallet-refungible NAME=erc_token::gen_impl OUTPUT=$(REFUNGIBLE_EVM_STUBS)/$@ ./.maintain/scripts/generate_sol.sh +UniqueRFT.sol: + PACKAGE=pallet-refungible NAME=erc::gen_iface OUTPUT=$(TESTS_API)/$@ ./.maintain/scripts/generate_sol.sh + PACKAGE=pallet-refungible NAME=erc::gen_impl OUTPUT=$(REFUNGIBLE_EVM_STUBS)/$@ ./.maintain/scripts/generate_sol.sh UniqueRefungibleToken.sol: PACKAGE=pallet-refungible NAME=erc_token::gen_iface OUTPUT=$(TESTS_API)/$@ ./.maintain/scripts/generate_sol.sh @@ -66,6 +65,10 @@ INPUT=$(REFUNGIBLE_EVM_STUBS)/$< OUTPUT=$(REFUNGIBLE_EVM_STUBS)/UniqueRefungibleToken.raw ./.maintain/scripts/compile_stub.sh INPUT=$(REFUNGIBLE_EVM_STUBS)/$< OUTPUT=$(RENFUNGIBLE_TOKEN_EVM_ABI) ./.maintain/scripts/generate_abi.sh +UniqueRefungible: UniqueRFT.sol + INPUT=$(REFUNGIBLE_EVM_STUBS)/$< OUTPUT=$(REFUNGIBLE_EVM_STUBS)/UniqueRFT.raw ./.maintain/scripts/compile_stub.sh + INPUT=$(REFUNGIBLE_EVM_STUBS)/$< OUTPUT=$(REFUNGIBLE_EVM_ABI) ./.maintain/scripts/generate_abi.sh + ContractHelpers: ContractHelpers.sol INPUT=$(CONTRACT_HELPERS_STUBS)/$< OUTPUT=$(CONTRACT_HELPERS_STUBS)/ContractHelpers.raw ./.maintain/scripts/compile_stub.sh INPUT=$(CONTRACT_HELPERS_STUBS)/$< OUTPUT=$(CONTRACT_HELPERS_ABI) ./.maintain/scripts/generate_abi.sh @@ -74,7 +77,7 @@ INPUT=$(COLLECTION_HELPER_STUBS)/$< OUTPUT=$(COLLECTION_HELPER_STUBS)/CollectionHelpers.raw ./.maintain/scripts/compile_stub.sh INPUT=$(COLLECTION_HELPER_STUBS)/$< OUTPUT=$(COLLECTION_HELPER_ABI) ./.maintain/scripts/generate_abi.sh -evm_stubs: UniqueFungible UniqueNFT UniqueRefungibleToken ContractHelpers CollectionHelpers +evm_stubs: UniqueFungible UniqueNFT UniqueRefungible UniqueRefungibleToken ContractHelpers CollectionHelpers .PHONY: _bench _bench: --- a/pallets/nonfungible/src/erc.rs +++ b/pallets/nonfungible/src/erc.rs @@ -238,8 +238,6 @@ } } else if !is_erc721() { return Err("tokenURI not set".into()); - } else if !is_erc721() { - return Err("tokenURI not set".into()); } if let Some(base_uri) = @@ -499,7 +497,7 @@ token_id: uint256, token_uri: string, ) -> Result { - let key = u_key(); + let key = url_key(); let permission = get_token_permission::(self.id, &key)?; if !permission.collection_admin { return Err("Operation is not allowed".into()); --- a/pallets/refungible/src/erc.rs +++ b/pallets/refungible/src/erc.rs @@ -38,7 +38,7 @@ where T::AccountId: From<[u8; 32]>, { - const CODE: &'static [u8] = include_bytes!("./stubs/UniqueRefungible.raw"); + const CODE: &'static [u8] = include_bytes!("./stubs/UniqueRFT.raw"); fn call( self, handle: &mut impl PrecompileHandle, --- /dev/null +++ b/pallets/refungible/src/stubs/UniqueRFT.sol @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: OTHER +// This code is automatically generated + +pragma solidity >=0.8.0 <0.9.0; + +// Common stubs holder +contract Dummy { + uint8 dummy; + string stub_error = "this contract is implemented in native"; +} + +contract ERC165 is Dummy { + function supportsInterface(bytes4 interfaceID) + external + view + returns (bool) + { + require(false, stub_error); + interfaceID; + return true; + } +} + +// Selector: 7d9262e6 +contract Collection is Dummy, ERC165 { + // Set collection property. + // + // @param key Property key. + // @param value Propery value. + // + // Selector: setCollectionProperty(string,bytes) 2f073f66 + function setCollectionProperty(string memory key, bytes memory value) + public + { + require(false, stub_error); + key; + value; + dummy = 0; + } + + // Delete collection property. + // + // @param key Property key. + // + // Selector: deleteCollectionProperty(string) 7b7debce + function deleteCollectionProperty(string memory key) public { + require(false, stub_error); + key; + dummy = 0; + } + + // Get collection property. + // + // @dev Throws error if key not found. + // + // @param key Property key. + // @return bytes The property corresponding to the key. + // + // Selector: collectionProperty(string) cf24fd6d + function collectionProperty(string memory key) + public + view + returns (bytes memory) + { + require(false, stub_error); + key; + dummy; + return hex""; + } + + // Set the sponsor of the collection. + // + // @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor. + // + // @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract. + // + // Selector: setCollectionSponsor(address) 7623402e + function setCollectionSponsor(address sponsor) public { + require(false, stub_error); + sponsor; + dummy = 0; + } + + // Collection sponsorship confirmation. + // + // @dev After setting the sponsor for the collection, it must be confirmed with this function. + // + // Selector: confirmCollectionSponsorship() 3c50e97a + function confirmCollectionSponsorship() public { + require(false, stub_error); + dummy = 0; + } + + // Set limits for the collection. + // @dev Throws error if limit not found. + // @param limit Name of the limit. Valid names: + // "accountTokenOwnershipLimit", + // "sponsoredDataSize", + // "sponsoredDataRateLimit", + // "tokenLimit", + // "sponsorTransferTimeout", + // "sponsorApproveTimeout" + // @param value Value of the limit. + // + // Selector: setCollectionLimit(string,uint32) 6a3841db + function setCollectionLimit(string memory limit, uint32 value) public { + require(false, stub_error); + limit; + value; + dummy = 0; + } + + // Set limits for the collection. + // @dev Throws error if limit not found. + // @param limit Name of the limit. Valid names: + // "ownerCanTransfer", + // "ownerCanDestroy", + // "transfersEnabled" + // @param value Value of the limit. + // + // Selector: setCollectionLimit(string,bool) 993b7fba + function setCollectionLimit(string memory limit, bool value) public { + require(false, stub_error); + limit; + value; + dummy = 0; + } + + // Get contract address. + // + // Selector: contractAddress() f6b4dfb4 + function contractAddress() public view returns (address) { + require(false, stub_error); + dummy; + return 0x0000000000000000000000000000000000000000; + } + + // Add collection admin by substrate address. + // @param new_admin Substrate administrator address. + // + // Selector: addCollectionAdminSubstrate(uint256) 5730062b + function addCollectionAdminSubstrate(uint256 newAdmin) public { + require(false, stub_error); + newAdmin; + dummy = 0; + } + + // Remove collection admin by substrate address. + // @param admin Substrate administrator address. + // + // Selector: removeCollectionAdminSubstrate(uint256) 4048fcf9 + function removeCollectionAdminSubstrate(uint256 admin) public { + require(false, stub_error); + admin; + dummy = 0; + } + + // Add collection admin. + // @param new_admin Address of the added administrator. + // + // Selector: addCollectionAdmin(address) 92e462c7 + function addCollectionAdmin(address newAdmin) public { + require(false, stub_error); + newAdmin; + dummy = 0; + } + + // Remove collection admin. + // + // @param new_admin Address of the removed administrator. + // + // Selector: removeCollectionAdmin(address) fafd7b42 + function removeCollectionAdmin(address admin) public { + require(false, stub_error); + admin; + dummy = 0; + } + + // Toggle accessibility of collection nesting. + // + // @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled' + // + // Selector: setCollectionNesting(bool) 112d4586 + function setCollectionNesting(bool enable) public { + require(false, stub_error); + enable; + dummy = 0; + } + + // Toggle accessibility of collection nesting. + // + // @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled' + // @param collections Addresses of collections that will be available for nesting. + // + // Selector: setCollectionNesting(bool,address[]) 64872396 + function setCollectionNesting(bool enable, address[] memory collections) + public + { + require(false, stub_error); + enable; + collections; + dummy = 0; + } + + // Set the collection access method. + // @param mode Access mode + // 0 for Normal + // 1 for AllowList + // + // Selector: setCollectionAccess(uint8) 41835d4c + function setCollectionAccess(uint8 mode) public { + require(false, stub_error); + mode; + dummy = 0; + } + + // Add the user to the allowed list. + // + // @param user Address of a trusted user. + // + // Selector: addToCollectionAllowList(address) 67844fe6 + function addToCollectionAllowList(address user) public { + require(false, stub_error); + user; + dummy = 0; + } + + // Remove the user from the allowed list. + // + // @param user Address of a removed user. + // + // Selector: removeFromCollectionAllowList(address) 85c51acb + function removeFromCollectionAllowList(address user) public { + require(false, stub_error); + user; + dummy = 0; + } + + // Switch permission for minting. + // + // @param mode Enable if "true". + // + // Selector: setCollectionMintMode(bool) 00018e84 + function setCollectionMintMode(bool mode) public { + require(false, stub_error); + mode; + dummy = 0; + } +} + +contract UniqueRFT is Dummy, ERC165, Collection {} --- a/pallets/refungible/src/stubs/UniqueRefungible.raw +++ /dev/null @@ -1 +0,0 @@ -TODO --- a/pallets/refungible/src/stubs/UniqueRefungibleToken.sol +++ b/pallets/refungible/src/stubs/UniqueRefungibleToken.sol @@ -33,6 +33,8 @@ // Selector: 942e8b22 contract ERC20 is Dummy, ERC165, ERC20Events { + // @return the name of the token. + // // Selector: name() 06fdde03 function name() public view returns (string memory) { require(false, stub_error); @@ -40,6 +42,8 @@ return ""; } + // @return the symbol of the token. + // // Selector: symbol() 95d89b41 function symbol() public view returns (string memory) { require(false, stub_error); @@ -47,6 +51,8 @@ return ""; } + // @dev Total number of tokens in existence + // // Selector: totalSupply() 18160ddd function totalSupply() public view returns (uint256) { require(false, stub_error); @@ -54,6 +60,8 @@ return 0; } + // @dev Not supported + // // Selector: decimals() 313ce567 function decimals() public view returns (uint8) { require(false, stub_error); @@ -61,6 +69,10 @@ return 0; } + // @dev Gets the balance of the specified address. + // @param owner The address to query the balance of. + // @return An uint256 representing the amount owned by the passed address. + // // Selector: balanceOf(address) 70a08231 function balanceOf(address owner) public view returns (uint256) { require(false, stub_error); @@ -69,6 +81,10 @@ return 0; } + // @dev Transfer token for a specified address + // @param to The address to transfer to. + // @param amount The amount to be transferred. + // // Selector: transfer(address,uint256) a9059cbb function transfer(address to, uint256 amount) public returns (bool) { require(false, stub_error); @@ -78,6 +94,11 @@ return false; } + // @dev Transfer tokens from one address to another + // @param from address The address which you want to send tokens from + // @param to address The address which you want to transfer to + // @param amount uint256 the amount of tokens to be transferred + // // Selector: transferFrom(address,address,uint256) 23b872dd function transferFrom( address from, @@ -92,6 +113,14 @@ return false; } + // @dev Approve the passed address to spend the specified amount of tokens on behalf of `msg.sender`. + // Beware that changing an allowance with this method brings the risk that someone may use both the old + // and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this + // race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: + // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + // @param spender The address which will spend the funds. + // @param amount The amount of tokens to be spent. + // // Selector: approve(address,uint256) 095ea7b3 function approve(address spender, uint256 amount) public returns (bool) { require(false, stub_error); @@ -101,6 +130,11 @@ return false; } + // @dev Function to check the amount of tokens that an owner allowed to a spender. + // @param owner address The address which owns the funds. + // @param spender address The address which will spend the funds. + // @return A uint256 specifying the amount of tokens still available for the spender. + // // Selector: allowance(address,address) dd62ed3e function allowance(address owner, address spender) public @@ -117,6 +151,11 @@ // Selector: ab8deb37 contract ERC20UniqueExtensions is Dummy, ERC165 { + // @dev Function that burns an amount of the token of a given account, + // deducting from the sender's allowance for said account. + // @param from The account whose tokens will be burnt. + // @param amount The amount that will be burnt. + // // Selector: burnFrom(address,uint256) 79cc6790 function burnFrom(address from, uint256 amount) public returns (bool) { require(false, stub_error); @@ -126,6 +165,10 @@ return false; } + // @dev Function that changes total amount of the tokens. + // Throws if `msg.sender` doesn't owns all of the tokens. + // @param amount New total amount of the tokens. + // // Selector: repartition(uint256) d2418ca7 function repartition(uint256 amount) public returns (bool) { require(false, stub_error); --- /dev/null +++ b/tests/src/eth/api/UniqueRFT.sol @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: OTHER +// This code is automatically generated + +pragma solidity >=0.8.0 <0.9.0; + +// Common stubs holder +interface Dummy { + +} + +interface ERC165 is Dummy { + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} + +// Selector: 7d9262e6 +interface Collection is Dummy, ERC165 { + // Set collection property. + // + // @param key Property key. + // @param value Propery value. + // + // Selector: setCollectionProperty(string,bytes) 2f073f66 + function setCollectionProperty(string memory key, bytes memory value) + external; + + // Delete collection property. + // + // @param key Property key. + // + // Selector: deleteCollectionProperty(string) 7b7debce + function deleteCollectionProperty(string memory key) external; + + // Get collection property. + // + // @dev Throws error if key not found. + // + // @param key Property key. + // @return bytes The property corresponding to the key. + // + // Selector: collectionProperty(string) cf24fd6d + function collectionProperty(string memory key) + external + view + returns (bytes memory); + + // Set the sponsor of the collection. + // + // @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor. + // + // @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract. + // + // Selector: setCollectionSponsor(address) 7623402e + function setCollectionSponsor(address sponsor) external; + + // Collection sponsorship confirmation. + // + // @dev After setting the sponsor for the collection, it must be confirmed with this function. + // + // Selector: confirmCollectionSponsorship() 3c50e97a + function confirmCollectionSponsorship() external; + + // Set limits for the collection. + // @dev Throws error if limit not found. + // @param limit Name of the limit. Valid names: + // "accountTokenOwnershipLimit", + // "sponsoredDataSize", + // "sponsoredDataRateLimit", + // "tokenLimit", + // "sponsorTransferTimeout", + // "sponsorApproveTimeout" + // @param value Value of the limit. + // + // Selector: setCollectionLimit(string,uint32) 6a3841db + function setCollectionLimit(string memory limit, uint32 value) external; + + // Set limits for the collection. + // @dev Throws error if limit not found. + // @param limit Name of the limit. Valid names: + // "ownerCanTransfer", + // "ownerCanDestroy", + // "transfersEnabled" + // @param value Value of the limit. + // + // Selector: setCollectionLimit(string,bool) 993b7fba + function setCollectionLimit(string memory limit, bool value) external; + + // Get contract address. + // + // Selector: contractAddress() f6b4dfb4 + function contractAddress() external view returns (address); + + // Add collection admin by substrate address. + // @param new_admin Substrate administrator address. + // + // Selector: addCollectionAdminSubstrate(uint256) 5730062b + function addCollectionAdminSubstrate(uint256 newAdmin) external; + + // Remove collection admin by substrate address. + // @param admin Substrate administrator address. + // + // Selector: removeCollectionAdminSubstrate(uint256) 4048fcf9 + function removeCollectionAdminSubstrate(uint256 admin) external; + + // Add collection admin. + // @param new_admin Address of the added administrator. + // + // Selector: addCollectionAdmin(address) 92e462c7 + function addCollectionAdmin(address newAdmin) external; + + // Remove collection admin. + // + // @param new_admin Address of the removed administrator. + // + // Selector: removeCollectionAdmin(address) fafd7b42 + function removeCollectionAdmin(address admin) external; + + // Toggle accessibility of collection nesting. + // + // @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled' + // + // Selector: setCollectionNesting(bool) 112d4586 + function setCollectionNesting(bool enable) external; + + // Toggle accessibility of collection nesting. + // + // @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled' + // @param collections Addresses of collections that will be available for nesting. + // + // Selector: setCollectionNesting(bool,address[]) 64872396 + function setCollectionNesting(bool enable, address[] memory collections) + external; + + // Set the collection access method. + // @param mode Access mode + // 0 for Normal + // 1 for AllowList + // + // Selector: setCollectionAccess(uint8) 41835d4c + function setCollectionAccess(uint8 mode) external; + + // Add the user to the allowed list. + // + // @param user Address of a trusted user. + // + // Selector: addToCollectionAllowList(address) 67844fe6 + function addToCollectionAllowList(address user) external; + + // Remove the user from the allowed list. + // + // @param user Address of a removed user. + // + // Selector: removeFromCollectionAllowList(address) 85c51acb + function removeFromCollectionAllowList(address user) external; + + // Switch permission for minting. + // + // @param mode Enable if "true". + // + // Selector: setCollectionMintMode(bool) 00018e84 + function setCollectionMintMode(bool mode) external; +} + +interface UniqueRFT is Dummy, ERC165, Collection {} --- a/tests/src/eth/api/UniqueRefungibleToken.sol +++ b/tests/src/eth/api/UniqueRefungibleToken.sol @@ -24,24 +24,45 @@ // Selector: 942e8b22 interface ERC20 is Dummy, ERC165, ERC20Events { + // @return the name of the token. + // // Selector: name() 06fdde03 function name() external view returns (string memory); + // @return the symbol of the token. + // // Selector: symbol() 95d89b41 function symbol() external view returns (string memory); + // @dev Total number of tokens in existence + // // Selector: totalSupply() 18160ddd function totalSupply() external view returns (uint256); + // @dev Not supported + // // Selector: decimals() 313ce567 function decimals() external view returns (uint8); + // @dev Gets the balance of the specified address. + // @param owner The address to query the balance of. + // @return An uint256 representing the amount owned by the passed address. + // // Selector: balanceOf(address) 70a08231 function balanceOf(address owner) external view returns (uint256); + // @dev Transfer token for a specified address + // @param to The address to transfer to. + // @param amount The amount to be transferred. + // // Selector: transfer(address,uint256) a9059cbb function transfer(address to, uint256 amount) external returns (bool); + // @dev Transfer tokens from one address to another + // @param from address The address which you want to send tokens from + // @param to address The address which you want to transfer to + // @param amount uint256 the amount of tokens to be transferred + // // Selector: transferFrom(address,address,uint256) 23b872dd function transferFrom( address from, @@ -49,9 +70,22 @@ uint256 amount ) external returns (bool); + // @dev Approve the passed address to spend the specified amount of tokens on behalf of `msg.sender`. + // Beware that changing an allowance with this method brings the risk that someone may use both the old + // and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this + // race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: + // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + // @param spender The address which will spend the funds. + // @param amount The amount of tokens to be spent. + // // Selector: approve(address,uint256) 095ea7b3 function approve(address spender, uint256 amount) external returns (bool); + // @dev Function to check the amount of tokens that an owner allowed to a spender. + // @param owner address The address which owns the funds. + // @param spender address The address which will spend the funds. + // @return A uint256 specifying the amount of tokens still available for the spender. + // // Selector: allowance(address,address) dd62ed3e function allowance(address owner, address spender) external @@ -61,9 +95,18 @@ // Selector: ab8deb37 interface ERC20UniqueExtensions is Dummy, ERC165 { + // @dev Function that burns an amount of the token of a given account, + // deducting from the sender's allowance for said account. + // @param from The account whose tokens will be burnt. + // @param amount The amount that will be burnt. + // // Selector: burnFrom(address,uint256) 79cc6790 function burnFrom(address from, uint256 amount) external returns (bool); + // @dev Function that changes total amount of the tokens. + // Throws if `msg.sender` doesn't owns all of the tokens. + // @param amount New total amount of the tokens. + // // Selector: repartition(uint256) d2418ca7 function repartition(uint256 amount) external returns (bool); } --- a/tests/src/eth/createRFTCollection.test.ts +++ b/tests/src/eth/createRFTCollection.test.ts @@ -69,27 +69,26 @@ .call()).to.be.true; }); - itWeb3.only('Set sponsorship', async ({api, web3, privateKeyWrapper}) => { + itWeb3('Set sponsorship', async ({api, web3, privateKeyWrapper}) => { const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const collectionHelpers = evmCollectionHelpers(web3, owner); let result = await collectionHelpers.methods.createRefungibleCollection('Sponsor collection', '1', '1').send(); const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const collectionEvm = evmCollection(web3, owner, collectionIdAddress); - result = await collectionEvm.methods.setCollectionSponsor(sponsor).call(); - // let collectionSub = (await getDetailedCollectionInfo(api, collectionId))!; - // expect(collectionSub.sponsorship.isUnconfirmed).to.be.true; - // const ss58Format = (api.registry.getChainProperties())!.toJSON().ss58Format; - // expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format))); - // await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor'); - // const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress); - // await sponsorCollection.methods.confirmCollectionSponsorship().send(); - // collectionSub = (await getDetailedCollectionInfo(api, collectionId))!; - // expect(collectionSub.sponsorship.isConfirmed).to.be.true; - // expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format))); + const collectionEvm = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'}); + result = await collectionEvm.methods.setCollectionSponsor(sponsor).send(); + let collectionSub = (await getDetailedCollectionInfo(api, collectionId))!; + expect(collectionSub.sponsorship.isUnconfirmed).to.be.true; + const ss58Format = (api.registry.getChainProperties())!.toJSON().ss58Format; + expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format))); + await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor'); + const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress); + await sponsorCollection.methods.confirmCollectionSponsorship().send(); + collectionSub = (await getDetailedCollectionInfo(api, collectionId))!; + expect(collectionSub.sponsorship.isConfirmed).to.be.true; + expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format))); }); - // fixtest itWeb3('Set limits', async ({api, web3, privateKeyWrapper}) => { const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const collectionHelpers = evmCollectionHelpers(web3, owner); @@ -107,7 +106,7 @@ transfersEnabled: false, }; - const collectionEvm = evmCollection(web3, owner, collectionIdAddress); + const collectionEvm = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'}); await collectionEvm.methods['setCollectionLimit(string,uint32)']('accountTokenOwnershipLimit', limits.accountTokenOwnershipLimit).send(); await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsoredDataSize', limits.sponsoredDataSize).send(); await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsoredDataRateLimit', limits.sponsoredDataRateLimit).send(); @@ -193,14 +192,13 @@ .call()).to.be.rejectedWith('NotSufficientFounds'); }); - // fixtest itWeb3('(!negative test!) Check owner', async ({api, web3, privateKeyWrapper}) => { const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const notOwner = await createEthAccount(web3); const collectionHelpers = evmCollectionHelpers(web3, owner); const result = await collectionHelpers.methods.createRefungibleCollection('A', 'A', 'A').send(); const {collectionIdAddress} = await getCollectionAddressFromResult(api, result); - const contractEvmFromNotOwner = evmCollection(web3, notOwner, collectionIdAddress); + const contractEvmFromNotOwner = evmCollection(web3, notOwner, collectionIdAddress, {type: 'ReFungible'}); const EXPECTED_ERROR = 'NoPermission'; { const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper); @@ -220,13 +218,12 @@ } }); - // fixtest itWeb3('(!negative test!) Set limits', async ({api, web3, privateKeyWrapper}) => { const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const collectionHelpers = evmCollectionHelpers(web3, owner); const result = await collectionHelpers.methods.createRefungibleCollection('Schema collection', 'A', 'A').send(); const {collectionIdAddress} = await getCollectionAddressFromResult(api, result); - const collectionEvm = evmCollection(web3, owner, collectionIdAddress); + const collectionEvm = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'}); await expect(collectionEvm.methods .setCollectionLimit('badLimit', 'true') .call()).to.be.rejectedWith('unknown boolean limit "badLimit"'); --- /dev/null +++ b/tests/src/eth/refungibleAbi.json @@ -0,0 +1,167 @@ +[ + { + "inputs": [ + { "internalType": "address", "name": "newAdmin", "type": "address" } + ], + "name": "addCollectionAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "newAdmin", "type": "uint256" } + ], + "name": "addCollectionAdminSubstrate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "user", "type": "address" } + ], + "name": "addToCollectionAllowList", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "string", "name": "key", "type": "string" }], + "name": "collectionProperty", + "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "confirmCollectionSponsorship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "contractAddress", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "string", "name": "key", "type": "string" }], + "name": "deleteCollectionProperty", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "admin", "type": "address" } + ], + "name": "removeCollectionAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "admin", "type": "uint256" } + ], + "name": "removeCollectionAdminSubstrate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "user", "type": "address" } + ], + "name": "removeFromCollectionAllowList", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint8", "name": "mode", "type": "uint8" }], + "name": "setCollectionAccess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string", "name": "limit", "type": "string" }, + { "internalType": "uint32", "name": "value", "type": "uint32" } + ], + "name": "setCollectionLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string", "name": "limit", "type": "string" }, + { "internalType": "bool", "name": "value", "type": "bool" } + ], + "name": "setCollectionLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "bool", "name": "mode", "type": "bool" }], + "name": "setCollectionMintMode", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }], + "name": "setCollectionNesting", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bool", "name": "enable", "type": "bool" }, + { + "internalType": "address[]", + "name": "collections", + "type": "address[]" + } + ], + "name": "setCollectionNesting", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string", "name": "key", "type": "string" }, + { "internalType": "bytes", "name": "value", "type": "bytes" } + ], + "name": "setCollectionProperty", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "sponsor", "type": "address" } + ], + "name": "setCollectionSponsor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" } + ], + "name": "supportsInterface", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + } +] --- a/tests/src/eth/util/helpers.ts +++ b/tests/src/eth/util/helpers.ts @@ -25,12 +25,13 @@ import Web3 from 'web3'; import config from '../../config'; import getBalance from '../../substrate/get-balance'; -import privateKey from '../../substrate/privateKey'; import usingApi, {submitTransactionAsync} from '../../substrate/substrate-api'; import waitNewBlocks from '../../substrate/wait-new-blocks'; -import {CrossAccountId, getDetailedCollectionInfo, getGenericResult, UNIQUE} from '../../util/helpers'; +import {CollectionMode, CrossAccountId, getDetailedCollectionInfo, getGenericResult, UNIQUE} from '../../util/helpers'; import collectionHelpersAbi from '../collectionHelpersAbi.json'; +import fungibleAbi from '../fungibleAbi.json'; import nonFungibleAbi from '../nonFungibleAbi.json'; +import refungibleAbi from '../refungibleAbi.json'; import contractHelpersAbi from './contractHelpersAbi.json'; export const GAS_ARGS = {gas: 2500000}; @@ -307,8 +308,26 @@ * @param caller - eth address * @returns */ -export function evmCollection(web3: Web3, caller: string, collection: string) { - return new web3.eth.Contract(nonFungibleAbi as any, collection, {from: caller, ...GAS_ARGS}); +export function evmCollection(web3: Web3, caller: string, collection: string, mode: CollectionMode = {type: 'NFT'}) { + let abi; + switch (mode.type) { + case 'Fungible': + abi = fungibleAbi; + break; + + case 'NFT': + abi = nonFungibleAbi; + break; + + case 'ReFungible': + abi = refungibleAbi; + break; + + default: + throw 'Bad collection mode'; + } + const contract = new web3.eth.Contract(abi as any, collection, {from: caller, ...GAS_ARGS}); + return contract; } /**