--- a/Cargo.lock +++ b/Cargo.lock @@ -6121,7 +6121,7 @@ [[package]] name = "pallet-fungible" -version = "0.1.6" +version = "0.1.7" dependencies = [ "ethereum", "evm-coder", @@ -6376,7 +6376,7 @@ [[package]] name = "pallet-nonfungible" -version = "0.1.8" +version = "0.1.9" dependencies = [ "ethereum", "evm-coder", @@ -6498,7 +6498,7 @@ [[package]] name = "pallet-refungible" -version = "0.2.7" +version = "0.2.8" dependencies = [ "derivative", "ethereum", --- a/pallets/fungible/CHANGELOG.md +++ b/pallets/fungible/CHANGELOG.md @@ -2,22 +2,32 @@ All notable changes to this project will be documented in this file. + + +## [0.1.7] - 2022-11-14 + +### Changed + +- Added `transfer_cross` in eth functions. + ## [0.1.6] - 2022-11-02 + ### Changed - - Use named structure `EthCrossAccount` in eth functions. +- Use named structure `EthCrossAccount` in eth functions. + ## [0.1.5] - 2022-08-29 ### Added - - Implementation of `mint` and `mint_bulk` methods for ERC20 API. +- Implementation of `mint` and `mint_bulk` methods for ERC20 API. ## [v0.1.4] - 2022-08-24 ### Change - - Add bound `AsRef<[u8; 32]>` to `T::CrossAccountId`. - +- Add bound `AsRef<[u8; 32]>` to `T::CrossAccountId`. + ## [v0.1.3] 2022-08-16 ### Other changes @@ -41,11 +51,11 @@ ### Fixed - - Issue with ItemCreated event containing total supply of tokens instead minted amount +- Issue with ItemCreated event containing total supply of tokens instead minted amount ## [0.1.1] - 2022-07-14 ### Added - - Implementation of RPC method `token_owners` returning 10 owners in no particular order. - This was an internal request to improve the web interface and support fractionalization event. +- Implementation of RPC method `token_owners` returning 10 owners in no particular order. + This was an internal request to improve the web interface and support fractionalization event. --- a/pallets/fungible/Cargo.toml +++ b/pallets/fungible/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-fungible" -version = "0.1.6" +version = "0.1.7" license = "GPLv3" edition = "2021" --- a/pallets/fungible/src/erc.rs +++ b/pallets/fungible/src/erc.rs @@ -93,6 +93,7 @@ >::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?; Ok(true) } + #[weight(>::transfer_from())] fn transfer_from( &mut self, @@ -238,6 +239,24 @@ Ok(true) } + #[weight(>::transfer())] + fn transfer_cross( + &mut self, + caller: caller, + to: EthCrossAccount, + amount: uint256, + ) -> Result { + let caller = T::CrossAccountId::from_eth(caller); + let to = to.into_sub_cross_account::()?; + let amount = amount.try_into().map_err(|_| "amount overflow")?; + let budget = self + .recorder + .weight_calls_budget(>::find_parent()); + + >::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?; + Ok(true) + } + #[weight(>::transfer_from())] fn transfer_from_cross( &mut self, --- a/pallets/fungible/src/stubs/UniqueFungible.sol +++ b/pallets/fungible/src/stubs/UniqueFungible.sol @@ -38,7 +38,7 @@ /// @param properties Vector of properties key/value pair. /// @dev EVM selector for this function is: 0x50b26b2a, /// or in textual repr: setCollectionProperties((string,bytes)[]) - function setCollectionProperties(Tuple14[] memory properties) public { + function setCollectionProperties(Tuple15[] memory properties) public { require(false, stub_error); properties; dummy = 0; @@ -87,11 +87,11 @@ /// @return Vector of properties key/value pairs. /// @dev EVM selector for this function is: 0x285fb8e6, /// or in textual repr: collectionProperties(string[]) - function collectionProperties(string[] memory keys) public view returns (Tuple14[] memory) { + function collectionProperties(string[] memory keys) public view returns (Tuple15[] memory) { require(false, stub_error); keys; dummy; - return new Tuple14[](0); + return new Tuple15[](0); } /// Set the sponsor of the collection. @@ -439,12 +439,12 @@ } /// @dev anonymous struct -struct Tuple14 { +struct Tuple15 { string field_0; bytes field_1; } -/// @dev the ERC-165 identifier for this interface is 0x032e5926 +/// @dev the ERC-165 identifier for this interface is 0x29f4dcd9 contract ERC20UniqueExtensions is Dummy, ERC165 { /// @dev EVM selector for this function is: 0x0ecd0ab0, /// or in textual repr: approveCross((address,uint256),uint256) @@ -497,6 +497,16 @@ return false; } + /// @dev EVM selector for this function is: 0x2ada85ff, + /// or in textual repr: transferCross((address,uint256),uint256) + function transferCross(EthCrossAccount memory to, uint256 amount) public returns (bool) { + require(false, stub_error); + to; + amount; + dummy = 0; + return false; + } + /// @dev EVM selector for this function is: 0xd5cf430b, /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256) function transferFromCross( --- a/pallets/nonfungible/CHANGELOG.md +++ b/pallets/nonfungible/CHANGELOG.md @@ -4,6 +4,12 @@ +## [0.1.9] - 2022-11-14 + +### Changed + +- Added `transfer_cross` in eth functions. + ## [v0.1.8] - 2022-11-11 ### Changed --- a/pallets/nonfungible/Cargo.toml +++ b/pallets/nonfungible/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-nonfungible" -version = "0.1.8" +version = "0.1.9" license = "GPLv3" edition = "2021" --- a/pallets/nonfungible/src/erc.rs +++ b/pallets/nonfungible/src/erc.rs @@ -738,7 +738,25 @@ >::transfer(self, &caller, &to, token, &budget).map_err(dispatch_to_evm::)?; Ok(()) } + + /// @notice Transfer ownership of an NFT + /// @dev Throws unless `msg.sender` is the current owner. Throws if `to` + /// is the zero address. Throws if `tokenId` is not a valid NFT. + /// @param to The new owner + /// @param tokenId The NFT to transfer + #[weight(>::transfer())] + fn transfer_cross(&mut self, caller: caller, to: EthCrossAccount, token_id: uint256) -> Result { + let caller = T::CrossAccountId::from_eth(caller); + let to = to.into_sub_cross_account::()?; + let token = token_id.try_into()?; + let budget = self + .recorder + .weight_calls_budget(>::find_parent()); + >::transfer(self, &caller, &to, token, &budget).map_err(dispatch_to_evm::)?; + Ok(()) + } + /// @notice Transfer ownership of an NFT from cross account address to cross account address /// @dev Throws unless `msg.sender` is the current owner. Throws if `to` /// is the zero address. Throws if `tokenId` is not a valid NFT. --- a/pallets/nonfungible/src/stubs/UniqueNFT.sol +++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol @@ -67,7 +67,7 @@ /// @param properties settable properties /// @dev EVM selector for this function is: 0x14ed3a6e, /// or in textual repr: setProperties(uint256,(string,bytes)[]) - function setProperties(uint256 tokenId, Tuple21[] memory properties) public { + function setProperties(uint256 tokenId, Tuple22[] memory properties) public { require(false, stub_error); tokenId; properties; @@ -137,7 +137,7 @@ /// @param properties Vector of properties key/value pair. /// @dev EVM selector for this function is: 0x50b26b2a, /// or in textual repr: setCollectionProperties((string,bytes)[]) - function setCollectionProperties(Tuple21[] memory properties) public { + function setCollectionProperties(Tuple22[] memory properties) public { require(false, stub_error); properties; dummy = 0; @@ -186,11 +186,11 @@ /// @return Vector of properties key/value pairs. /// @dev EVM selector for this function is: 0x285fb8e6, /// or in textual repr: collectionProperties(string[]) - function collectionProperties(string[] memory keys) public view returns (Tuple21[] memory) { + function collectionProperties(string[] memory keys) public view returns (Tuple22[] memory) { require(false, stub_error); keys; dummy; - return new Tuple21[](0); + return new Tuple22[](0); } /// Set the sponsor of the collection. @@ -251,10 +251,10 @@ /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. /// @dev EVM selector for this function is: 0x6ec0a9f1, /// or in textual repr: collectionSponsor() - function collectionSponsor() public view returns (Tuple24 memory) { + function collectionSponsor() public view returns (Tuple25 memory) { require(false, stub_error); dummy; - return Tuple24(0x0000000000000000000000000000000000000000, 0); + return Tuple25(0x0000000000000000000000000000000000000000, 0); } /// Set limits for the collection. @@ -538,13 +538,13 @@ } /// @dev anonymous struct -struct Tuple24 { +struct Tuple25 { address field_0; uint256 field_1; } /// @dev anonymous struct -struct Tuple21 { +struct Tuple22 { string field_0; bytes field_1; } @@ -693,7 +693,7 @@ } /// @title Unique extensions for ERC721. -/// @dev the ERC-165 identifier for this interface is 0x244543ee +/// @dev the ERC-165 identifier for this interface is 0x0e9fc611 contract ERC721UniqueExtensions is Dummy, ERC165 { /// @notice A descriptive name for a collection of NFTs in this contract /// @dev EVM selector for this function is: 0x06fdde03, @@ -742,6 +742,20 @@ dummy = 0; } + /// @notice Transfer ownership of an NFT + /// @dev Throws unless `msg.sender` is the current owner. Throws if `to` + /// is the zero address. Throws if `tokenId` is not a valid NFT. + /// @param to The new owner + /// @param tokenId The NFT to transfer + /// @dev EVM selector for this function is: 0x2ada85ff, + /// or in textual repr: transferCross((address,uint256),uint256) + function transferCross(EthCrossAccount memory to, uint256 tokenId) public { + require(false, stub_error); + to; + tokenId; + dummy = 0; + } + /// @notice Transfer ownership of an NFT from cross account address to cross account address /// @dev Throws unless `msg.sender` is the current owner. Throws if `to` /// is the zero address. Throws if `tokenId` is not a valid NFT. @@ -822,7 +836,7 @@ // /// @param tokens array of pairs of token ID and token URI for minted tokens // /// @dev EVM selector for this function is: 0x36543006, // /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[]) - // function mintBulkWithTokenURI(address to, Tuple10[] memory tokens) public returns (bool) { + // function mintBulkWithTokenURI(address to, Tuple11[] memory tokens) public returns (bool) { // require(false, stub_error); // to; // tokens; @@ -833,7 +847,7 @@ } /// @dev anonymous struct -struct Tuple10 { +struct Tuple11 { uint256 field_0; string field_1; } --- a/pallets/refungible/CHANGELOG.md +++ b/pallets/refungible/CHANGELOG.md @@ -4,6 +4,12 @@ +## [0.2.8] - 2022-11-14 + +### Changed + +- Added `transfer_cross` in eth functions. + ## [v0.2.7] - 2022-11-11 ### Changed --- a/pallets/refungible/Cargo.toml +++ b/pallets/refungible/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-refungible" -version = "0.2.7" +version = "0.2.8" license = "GPLv3" edition = "2021" --- a/pallets/refungible/src/erc.rs +++ b/pallets/refungible/src/erc.rs @@ -756,6 +756,29 @@ /// @param to The new owner /// @param tokenId The RFT to transfer #[weight(>::transfer_creating_removing())] + fn transfer_cross(&mut self, caller: caller, to: EthCrossAccount, token_id: uint256) -> Result { + let caller = T::CrossAccountId::from_eth(caller); + let to = to.into_sub_cross_account::()?; + let token = token_id.try_into()?; + let budget = self + .recorder + .weight_calls_budget(>::find_parent()); + + let balance = balance(self, token, &caller)?; + ensure_single_owner(self, token, balance)?; + + >::transfer(self, &caller, &to, token, balance, &budget) + .map_err(dispatch_to_evm::)?; + Ok(()) + } + + /// @notice Transfer ownership of an RFT + /// @dev Throws unless `msg.sender` is the current owner. Throws if `to` + /// is the zero address. Throws if `tokenId` is not a valid RFT. + /// Throws if RFT pieces have multiple owners. + /// @param to The new owner + /// @param tokenId The RFT to transfer + #[weight(>::transfer_creating_removing())] fn transfer_from_cross( &mut self, caller: caller, --- a/pallets/refungible/src/stubs/UniqueRefungible.sol +++ b/pallets/refungible/src/stubs/UniqueRefungible.sol @@ -67,7 +67,7 @@ /// @param properties settable properties /// @dev EVM selector for this function is: 0x14ed3a6e, /// or in textual repr: setProperties(uint256,(string,bytes)[]) - function setProperties(uint256 tokenId, Tuple20[] memory properties) public { + function setProperties(uint256 tokenId, Tuple21[] memory properties) public { require(false, stub_error); tokenId; properties; @@ -137,7 +137,7 @@ /// @param properties Vector of properties key/value pair. /// @dev EVM selector for this function is: 0x50b26b2a, /// or in textual repr: setCollectionProperties((string,bytes)[]) - function setCollectionProperties(Tuple20[] memory properties) public { + function setCollectionProperties(Tuple21[] memory properties) public { require(false, stub_error); properties; dummy = 0; @@ -186,11 +186,11 @@ /// @return Vector of properties key/value pairs. /// @dev EVM selector for this function is: 0x285fb8e6, /// or in textual repr: collectionProperties(string[]) - function collectionProperties(string[] memory keys) public view returns (Tuple20[] memory) { + function collectionProperties(string[] memory keys) public view returns (Tuple21[] memory) { require(false, stub_error); keys; dummy; - return new Tuple20[](0); + return new Tuple21[](0); } /// Set the sponsor of the collection. @@ -251,10 +251,10 @@ /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. /// @dev EVM selector for this function is: 0x6ec0a9f1, /// or in textual repr: collectionSponsor() - function collectionSponsor() public view returns (Tuple23 memory) { + function collectionSponsor() public view returns (Tuple24 memory) { require(false, stub_error); dummy; - return Tuple23(0x0000000000000000000000000000000000000000, 0); + return Tuple24(0x0000000000000000000000000000000000000000, 0); } /// Set limits for the collection. @@ -538,13 +538,13 @@ } /// @dev anonymous struct -struct Tuple23 { +struct Tuple24 { address field_0; uint256 field_1; } /// @dev anonymous struct -struct Tuple20 { +struct Tuple21 { string field_0; bytes field_1; } @@ -691,7 +691,7 @@ } /// @title Unique extensions for ERC721. -/// @dev the ERC-165 identifier for this interface is 0x81feb398 +/// @dev the ERC-165 identifier for this interface is 0xab243667 contract ERC721UniqueExtensions is Dummy, ERC165 { /// @notice A descriptive name for a collection of NFTs in this contract /// @dev EVM selector for this function is: 0x06fdde03, @@ -732,6 +732,21 @@ /// Throws if RFT pieces have multiple owners. /// @param to The new owner /// @param tokenId The RFT to transfer + /// @dev EVM selector for this function is: 0x2ada85ff, + /// or in textual repr: transferCross((address,uint256),uint256) + function transferCross(EthCrossAccount memory to, uint256 tokenId) public { + require(false, stub_error); + to; + tokenId; + dummy = 0; + } + + /// @notice Transfer ownership of an RFT + /// @dev Throws unless `msg.sender` is the current owner. Throws if `to` + /// is the zero address. Throws if `tokenId` is not a valid RFT. + /// Throws if RFT pieces have multiple owners. + /// @param to The new owner + /// @param tokenId The RFT to transfer /// @dev EVM selector for this function is: 0xd5cf430b, /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256) function transferFromCross( @@ -809,7 +824,7 @@ // /// @param tokens array of pairs of token ID and token URI for minted tokens // /// @dev EVM selector for this function is: 0x36543006, // /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[]) - // function mintBulkWithTokenURI(address to, Tuple9[] memory tokens) public returns (bool) { + // function mintBulkWithTokenURI(address to, Tuple10[] memory tokens) public returns (bool) { // require(false, stub_error); // to; // tokens; @@ -831,7 +846,7 @@ } /// @dev anonymous struct -struct Tuple9 { +struct Tuple10 { uint256 field_0; string field_1; } --- a/tests/src/eth/api/UniqueFungible.sol +++ b/tests/src/eth/api/UniqueFungible.sol @@ -28,7 +28,7 @@ /// @param properties Vector of properties key/value pair. /// @dev EVM selector for this function is: 0x50b26b2a, /// or in textual repr: setCollectionProperties((string,bytes)[]) - function setCollectionProperties(Tuple14[] memory properties) external; + function setCollectionProperties(Tuple15[] memory properties) external; /// Delete collection property. /// @@ -60,7 +60,7 @@ /// @return Vector of properties key/value pairs. /// @dev EVM selector for this function is: 0x285fb8e6, /// or in textual repr: collectionProperties(string[]) - function collectionProperties(string[] memory keys) external view returns (Tuple14[] memory); + function collectionProperties(string[] memory keys) external view returns (Tuple15[] memory); /// Set the sponsor of the collection. /// @@ -287,12 +287,12 @@ } /// @dev anonymous struct -struct Tuple14 { +struct Tuple15 { string field_0; bytes field_1; } -/// @dev the ERC-165 identifier for this interface is 0x032e5926 +/// @dev the ERC-165 identifier for this interface is 0x29f4dcd9 interface ERC20UniqueExtensions is Dummy, ERC165 { /// @dev EVM selector for this function is: 0x0ecd0ab0, /// or in textual repr: approveCross((address,uint256),uint256) @@ -322,6 +322,10 @@ /// or in textual repr: mintBulk((address,uint256)[]) function mintBulk(Tuple8[] memory amounts) external returns (bool); + /// @dev EVM selector for this function is: 0x2ada85ff, + /// or in textual repr: transferCross((address,uint256),uint256) + function transferCross(EthCrossAccount memory to, uint256 amount) external returns (bool); + /// @dev EVM selector for this function is: 0xd5cf430b, /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256) function transferFromCross( --- a/tests/src/eth/api/UniqueNFT.sol +++ b/tests/src/eth/api/UniqueNFT.sol @@ -49,7 +49,7 @@ /// @param properties settable properties /// @dev EVM selector for this function is: 0x14ed3a6e, /// or in textual repr: setProperties(uint256,(string,bytes)[]) - function setProperties(uint256 tokenId, Tuple21[] memory properties) external; + function setProperties(uint256 tokenId, Tuple22[] memory properties) external; // /// @notice Delete token property value. // /// @dev Throws error if `msg.sender` has no permission to edit the property. @@ -93,7 +93,7 @@ /// @param properties Vector of properties key/value pair. /// @dev EVM selector for this function is: 0x50b26b2a, /// or in textual repr: setCollectionProperties((string,bytes)[]) - function setCollectionProperties(Tuple21[] memory properties) external; + function setCollectionProperties(Tuple22[] memory properties) external; /// Delete collection property. /// @@ -125,7 +125,7 @@ /// @return Vector of properties key/value pairs. /// @dev EVM selector for this function is: 0x285fb8e6, /// or in textual repr: collectionProperties(string[]) - function collectionProperties(string[] memory keys) external view returns (Tuple21[] memory); + function collectionProperties(string[] memory keys) external view returns (Tuple22[] memory); /// Set the sponsor of the collection. /// @@ -167,7 +167,7 @@ /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. /// @dev EVM selector for this function is: 0x6ec0a9f1, /// or in textual repr: collectionSponsor() - function collectionSponsor() external view returns (Tuple24 memory); + function collectionSponsor() external view returns (Tuple25 memory); /// Set limits for the collection. /// @dev Throws error if limit not found. @@ -352,13 +352,13 @@ } /// @dev anonymous struct -struct Tuple24 { +struct Tuple25 { address field_0; uint256 field_1; } /// @dev anonymous struct -struct Tuple21 { +struct Tuple22 { string field_0; bytes field_1; } @@ -458,7 +458,7 @@ } /// @title Unique extensions for ERC721. -/// @dev the ERC-165 identifier for this interface is 0x244543ee +/// @dev the ERC-165 identifier for this interface is 0x0e9fc611 interface ERC721UniqueExtensions is Dummy, ERC165 { /// @notice A descriptive name for a collection of NFTs in this contract /// @dev EVM selector for this function is: 0x06fdde03, @@ -489,6 +489,15 @@ /// or in textual repr: transfer(address,uint256) function transfer(address to, uint256 tokenId) external; + /// @notice Transfer ownership of an NFT + /// @dev Throws unless `msg.sender` is the current owner. Throws if `to` + /// is the zero address. Throws if `tokenId` is not a valid NFT. + /// @param to The new owner + /// @param tokenId The NFT to transfer + /// @dev EVM selector for this function is: 0x2ada85ff, + /// or in textual repr: transferCross((address,uint256),uint256) + function transferCross(EthCrossAccount memory to, uint256 tokenId) external; + /// @notice Transfer ownership of an NFT from cross account address to cross account address /// @dev Throws unless `msg.sender` is the current owner. Throws if `to` /// is the zero address. Throws if `tokenId` is not a valid NFT. @@ -543,12 +552,12 @@ // /// @param tokens array of pairs of token ID and token URI for minted tokens // /// @dev EVM selector for this function is: 0x36543006, // /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[]) - // function mintBulkWithTokenURI(address to, Tuple10[] memory tokens) external returns (bool); + // function mintBulkWithTokenURI(address to, Tuple11[] memory tokens) external returns (bool); } /// @dev anonymous struct -struct Tuple10 { +struct Tuple11 { uint256 field_0; string field_1; } --- a/tests/src/eth/api/UniqueRefungible.sol +++ b/tests/src/eth/api/UniqueRefungible.sol @@ -49,7 +49,7 @@ /// @param properties settable properties /// @dev EVM selector for this function is: 0x14ed3a6e, /// or in textual repr: setProperties(uint256,(string,bytes)[]) - function setProperties(uint256 tokenId, Tuple20[] memory properties) external; + function setProperties(uint256 tokenId, Tuple21[] memory properties) external; // /// @notice Delete token property value. // /// @dev Throws error if `msg.sender` has no permission to edit the property. @@ -93,7 +93,7 @@ /// @param properties Vector of properties key/value pair. /// @dev EVM selector for this function is: 0x50b26b2a, /// or in textual repr: setCollectionProperties((string,bytes)[]) - function setCollectionProperties(Tuple20[] memory properties) external; + function setCollectionProperties(Tuple21[] memory properties) external; /// Delete collection property. /// @@ -125,7 +125,7 @@ /// @return Vector of properties key/value pairs. /// @dev EVM selector for this function is: 0x285fb8e6, /// or in textual repr: collectionProperties(string[]) - function collectionProperties(string[] memory keys) external view returns (Tuple20[] memory); + function collectionProperties(string[] memory keys) external view returns (Tuple21[] memory); /// Set the sponsor of the collection. /// @@ -167,7 +167,7 @@ /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. /// @dev EVM selector for this function is: 0x6ec0a9f1, /// or in textual repr: collectionSponsor() - function collectionSponsor() external view returns (Tuple23 memory); + function collectionSponsor() external view returns (Tuple24 memory); /// Set limits for the collection. /// @dev Throws error if limit not found. @@ -352,13 +352,13 @@ } /// @dev anonymous struct -struct Tuple23 { +struct Tuple24 { address field_0; uint256 field_1; } /// @dev anonymous struct -struct Tuple20 { +struct Tuple21 { string field_0; bytes field_1; } @@ -456,7 +456,7 @@ } /// @title Unique extensions for ERC721. -/// @dev the ERC-165 identifier for this interface is 0x81feb398 +/// @dev the ERC-165 identifier for this interface is 0xab243667 interface ERC721UniqueExtensions is Dummy, ERC165 { /// @notice A descriptive name for a collection of NFTs in this contract /// @dev EVM selector for this function is: 0x06fdde03, @@ -484,6 +484,16 @@ /// Throws if RFT pieces have multiple owners. /// @param to The new owner /// @param tokenId The RFT to transfer + /// @dev EVM selector for this function is: 0x2ada85ff, + /// or in textual repr: transferCross((address,uint256),uint256) + function transferCross(EthCrossAccount memory to, uint256 tokenId) external; + + /// @notice Transfer ownership of an RFT + /// @dev Throws unless `msg.sender` is the current owner. Throws if `to` + /// is the zero address. Throws if `tokenId` is not a valid RFT. + /// Throws if RFT pieces have multiple owners. + /// @param to The new owner + /// @param tokenId The RFT to transfer /// @dev EVM selector for this function is: 0xd5cf430b, /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256) function transferFromCross( @@ -535,7 +545,7 @@ // /// @param tokens array of pairs of token ID and token URI for minted tokens // /// @dev EVM selector for this function is: 0x36543006, // /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[]) - // function mintBulkWithTokenURI(address to, Tuple9[] memory tokens) external returns (bool); + // function mintBulkWithTokenURI(address to, Tuple10[] memory tokens) external returns (bool); /// Returns EVM address for refungible token /// @@ -546,7 +556,7 @@ } /// @dev anonymous struct -struct Tuple9 { +struct Tuple10 { uint256 field_0; string field_1; } --- a/tests/src/eth/fungible.test.ts +++ b/tests/src/eth/fungible.test.ts @@ -230,6 +230,37 @@ } }); + itEth('Can perform transferCross()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const receiver = await helper.eth.createAccountWithBalance(donor); + const to = helper.ethCrossAccount.fromAddress(receiver); + const collection = await helper.ft.mintCollection(alice); + await collection.mint(alice, 200n, {Ethereum: owner}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner); + + { + const result = await contract.methods.transferCross(to, 50).send({from: owner}); + + const event = result.events.Transfer; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal(owner); + expect(event.returnValues.to).to.be.equal(receiver); + expect(event.returnValues.value).to.be.equal('50'); + } + + { + const balance = await contract.methods.balanceOf(owner).call(); + expect(+balance).to.equal(150); + } + + { + const balance = await contract.methods.balanceOf(receiver).call(); + expect(+balance).to.equal(50); + } + }); + itEth('Can perform transfer()', async ({helper}) => { const owner = await helper.eth.createAccountWithBalance(donor); const receiver = await helper.eth.createAccountWithBalance(donor); --- a/tests/src/eth/fungibleAbi.json +++ b/tests/src/eth/fungibleAbi.json @@ -239,7 +239,7 @@ { "internalType": "string", "name": "field_0", "type": "string" }, { "internalType": "bytes", "name": "field_1", "type": "bytes" } ], - "internalType": "struct Tuple14[]", + "internalType": "struct Tuple15[]", "name": "", "type": "tuple[]" } @@ -496,7 +496,7 @@ { "internalType": "string", "name": "field_0", "type": "string" }, { "internalType": "bytes", "name": "field_1", "type": "bytes" } ], - "internalType": "struct Tuple14[]", + "internalType": "struct Tuple15[]", "name": "properties", "type": "tuple[]" } @@ -594,6 +594,24 @@ }, { "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct EthCrossAccount", + "name": "to", + "type": "tuple" + }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transferCross", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ { "internalType": "address", "name": "from", "type": "address" }, { "internalType": "address", "name": "to", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } --- a/tests/src/eth/nonFungible.test.ts +++ b/tests/src/eth/nonFungible.test.ts @@ -402,6 +402,37 @@ expect(+balance).to.equal(1); } }); + + itEth('Can perform transferCross()', async ({helper}) => { + const collection = await helper.nft.mintCollection(minter, {}); + const owner = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + const to = helper.ethCrossAccount.fromAddress(receiver); + const {tokenId} = await collection.mintToken(minter, {Ethereum: owner}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + + { + const result = await contract.methods.transferCross(to, tokenId).send({from: owner}); + + const event = result.events.Transfer; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal(owner); + expect(event.returnValues.to).to.be.equal(receiver); + expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`); + } + + { + const balance = await contract.methods.balanceOf(owner).call(); + expect(+balance).to.equal(0); + } + + { + const balance = await contract.methods.balanceOf(receiver).call(); + expect(+balance).to.equal(1); + } + }); }); describe('NFT: Fees', () => { --- a/tests/src/eth/nonFungibleAbi.json +++ b/tests/src/eth/nonFungibleAbi.json @@ -269,7 +269,7 @@ { "internalType": "string", "name": "field_0", "type": "string" }, { "internalType": "bytes", "name": "field_1", "type": "bytes" } ], - "internalType": "struct Tuple21[]", + "internalType": "struct Tuple22[]", "name": "", "type": "tuple[]" } @@ -293,7 +293,7 @@ { "internalType": "address", "name": "field_0", "type": "address" }, { "internalType": "uint256", "name": "field_1", "type": "uint256" } ], - "internalType": "struct Tuple24", + "internalType": "struct Tuple25", "name": "", "type": "tuple" } @@ -611,7 +611,7 @@ { "internalType": "string", "name": "field_0", "type": "string" }, { "internalType": "bytes", "name": "field_1", "type": "bytes" } ], - "internalType": "struct Tuple21[]", + "internalType": "struct Tuple22[]", "name": "properties", "type": "tuple[]" } @@ -682,7 +682,7 @@ { "internalType": "string", "name": "field_0", "type": "string" }, { "internalType": "bytes", "name": "field_1", "type": "bytes" } ], - "internalType": "struct Tuple21[]", + "internalType": "struct Tuple22[]", "name": "properties", "type": "tuple[]" } @@ -778,6 +778,24 @@ }, { "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct EthCrossAccount", + "name": "to", + "type": "tuple" + }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "transferCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ { "internalType": "address", "name": "from", "type": "address" }, { "internalType": "address", "name": "to", "type": "address" }, { "internalType": "uint256", "name": "tokenId", "type": "uint256" } --- a/tests/src/eth/reFungible.test.ts +++ b/tests/src/eth/reFungible.test.ts @@ -359,6 +359,37 @@ expect(+balance).to.equal(1); } }); + + itEth('Can perform transferCross()', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + const to = helper.ethCrossAccount.fromAddress(receiver); + const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry', '6', '6'); + const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller); + + const result = await contract.methods.mint(caller).send(); + const tokenId = result.events.Transfer.returnValues.tokenId; + + { + const result = await contract.methods.transferCross(to, tokenId).send({from: caller}); + + const event = result.events.Transfer; + expect(event.address).to.equal(collectionAddress); + expect(event.returnValues.from).to.equal(caller); + expect(event.returnValues.to).to.equal(receiver); + expect(event.returnValues.tokenId).to.equal(tokenId.toString()); + } + + { + const balance = await contract.methods.balanceOf(caller).call(); + expect(+balance).to.equal(0); + } + + { + const balance = await contract.methods.balanceOf(receiver).call(); + expect(+balance).to.equal(1); + } + }); itEth('transfer event on transfer from partial ownership to full ownership', async ({helper}) => { const caller = await helper.eth.createAccountWithBalance(donor); --- a/tests/src/eth/reFungibleAbi.json +++ b/tests/src/eth/reFungibleAbi.json @@ -251,7 +251,7 @@ { "internalType": "string", "name": "field_0", "type": "string" }, { "internalType": "bytes", "name": "field_1", "type": "bytes" } ], - "internalType": "struct Tuple20[]", + "internalType": "struct Tuple21[]", "name": "", "type": "tuple[]" } @@ -275,7 +275,7 @@ { "internalType": "address", "name": "field_0", "type": "address" }, { "internalType": "uint256", "name": "field_1", "type": "uint256" } ], - "internalType": "struct Tuple23", + "internalType": "struct Tuple24", "name": "", "type": "tuple" } @@ -593,7 +593,7 @@ { "internalType": "string", "name": "field_0", "type": "string" }, { "internalType": "bytes", "name": "field_1", "type": "bytes" } ], - "internalType": "struct Tuple20[]", + "internalType": "struct Tuple21[]", "name": "properties", "type": "tuple[]" } @@ -664,7 +664,7 @@ { "internalType": "string", "name": "field_0", "type": "string" }, { "internalType": "bytes", "name": "field_1", "type": "bytes" } ], - "internalType": "struct Tuple20[]", + "internalType": "struct Tuple21[]", "name": "properties", "type": "tuple[]" } @@ -769,6 +769,24 @@ }, { "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct EthCrossAccount", + "name": "to", + "type": "tuple" + }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "transferCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ { "internalType": "address", "name": "from", "type": "address" }, { "internalType": "address", "name": "to", "type": "address" }, { "internalType": "uint256", "name": "tokenId", "type": "uint256" }