git.delta.rocks / unique-network / refs/commits / fc406280aa2f

difftreelog

feature/mint-for-fungible-token

Grigoriy Simonov2022-08-18parent: #eadc594.patch.diff
in: master

5 files changed

modifiedpallets/fungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/fungible/src/erc.rs
+++ b/pallets/fungible/src/erc.rs
@@ -129,7 +129,23 @@
 	}
 }
 
-#[solidity_interface(name = ERC20UniqueExtensions)]
+#[solidity_interface(name = "ERC20Mintable")]
+impl<T: Config> FungibleHandle<T> {
+	#[weight(<SelfWeightOf<T>>::create_item())]
+	fn mint(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {
+		let caller = T::CrossAccountId::from_eth(caller);
+		let to = T::CrossAccountId::from_eth(to);
+		let amount = amount.try_into().map_err(|_| "amount overflow")?;
+		let budget = self
+			.recorder
+			.weight_calls_budget(<StructureWeight<T>>::find_parent());
+		<Pallet<T>>::create_item(&self, &caller, (to, amount), &budget)
+			.map_err(dispatch_to_evm::<T>)?;
+		Ok(true)
+	}
+}
+
+#[solidity_interface(name = "ERC20UniqueExtensions")]
 impl<T: Config> FungibleHandle<T> {
 	#[weight(<SelfWeightOf<T>>::burn_from())]
 	fn burn_from(&mut self, caller: caller, from: address, amount: uint256) -> Result<bool> {
@@ -144,12 +160,29 @@
 			.map_err(dispatch_to_evm::<T>)?;
 		Ok(true)
 	}
+
+	#[weight(<SelfWeightOf<T>>::create_multiple_items_ex(amounts.len() as u32))]
+	fn mint_bulk(&mut self, caller: caller, amounts: Vec<(address, uint256)>) -> Result<bool> {
+		let caller = T::CrossAccountId::from_eth(caller);
+		let budget = self
+			.recorder
+			.weight_calls_budget(<StructureWeight<T>>::find_parent());
+		let amounts = amounts
+			.into_iter()
+			.map(|(to, amount)| Ok((T::CrossAccountId::from_eth(to), amount.try_into().map_err(|_| "amount overflow")?)))
+			.collect::<Result<_>>()?;
+
+		<Pallet<T>>::create_multiple_items(&self, &caller, amounts, &budget)
+			.map_err(dispatch_to_evm::<T>)?;
+		Ok(true)
+	}
 }
 
 #[solidity_interface(
 	name = UniqueFungible,
 	is(
 		ERC20,
+		ERC20Mintable,
 		ERC20UniqueExtensions,
 		Collection(common_mut, CollectionHandle<T>),
 	)
modifiedpallets/fungible/src/stubs/UniqueFungible.soldiffbeforeafterboth
--- a/pallets/fungible/src/stubs/UniqueFungible.sol
+++ b/pallets/fungible/src/stubs/UniqueFungible.sol
@@ -3,7 +3,13 @@
 
 pragma solidity >=0.8.0 <0.9.0;
 
-/// @dev common stubs holder
+// Anonymous struct
+struct Tuple0 {
+	address field_0;
+	uint256 field_1;
+}
+
+// Common stubs holder
 contract Dummy {
 	uint8 dummy;
 	string stub_error = "this contract is implemented in native";
@@ -21,8 +27,49 @@
 	}
 }
 
-/// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0xe54be640
+// Inline
+contract ERC20Events {
+	event Transfer(address indexed from, address indexed to, uint256 value);
+	event Approval(
+		address indexed owner,
+		address indexed spender,
+		uint256 value
+	);
+}
+
+// Selector: 40c10f19
+contract ERC20Mintable is Dummy, ERC165 {
+	// Selector: mint(address,uint256) 40c10f19
+	function mint(address to, uint256 amount) public returns (bool) {
+		require(false, stub_error);
+		to;
+		amount;
+		dummy = 0;
+		return false;
+	}
+}
+
+// Selector: 63034ac5
+contract ERC20UniqueExtensions is Dummy, ERC165 {
+	// Selector: burnFrom(address,uint256) 79cc6790
+	function burnFrom(address from, uint256 amount) public returns (bool) {
+		require(false, stub_error);
+		from;
+		amount;
+		dummy = 0;
+		return false;
+	}
+
+	// Selector: mintBulk((address,uint256)[]) 1acf2d55
+	function mintBulk(Tuple0[] memory amounts) public returns (bool) {
+		require(false, stub_error);
+		amounts;
+		dummy = 0;
+		return false;
+	}
+}
+
+// Selector: 6cf113cd
 contract Collection is Dummy, ERC165 {
 	/// Set collection property.
 	///
@@ -476,6 +523,7 @@
 	Dummy,
 	ERC165,
 	ERC20,
+	ERC20Mintable,
 	ERC20UniqueExtensions,
 	Collection
 {}
modifiedtests/src/eth/api/UniqueFungible.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueFungible.sol
+++ b/tests/src/eth/api/UniqueFungible.sol
@@ -3,7 +3,13 @@
 
 pragma solidity >=0.8.0 <0.9.0;
 
-/// @dev common stubs holder
+// Anonymous struct
+struct Tuple0 {
+	address field_0;
+	uint256 field_1;
+}
+
+// Common stubs holder
 interface Dummy {
 
 }
@@ -12,8 +18,32 @@
 	function supportsInterface(bytes4 interfaceID) external view returns (bool);
 }
 
-/// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0xe54be640
+// Inline
+interface ERC20Events {
+	event Transfer(address indexed from, address indexed to, uint256 value);
+	event Approval(
+		address indexed owner,
+		address indexed spender,
+		uint256 value
+	);
+}
+
+// Selector: 40c10f19
+interface ERC20Mintable is Dummy, ERC165 {
+	// Selector: mint(address,uint256) 40c10f19
+	function mint(address to, uint256 amount) external returns (bool);
+}
+
+// Selector: 63034ac5
+interface ERC20UniqueExtensions is Dummy, ERC165 {
+	// Selector: burnFrom(address,uint256) 79cc6790
+	function burnFrom(address from, uint256 amount) external returns (bool);
+
+	// Selector: mintBulk((address,uint256)[]) 1acf2d55
+	function mintBulk(Tuple0[] memory amounts) external returns (bool);
+}
+
+// Selector: 6cf113cd
 interface Collection is Dummy, ERC165 {
 	/// Set collection property.
 	///
@@ -207,30 +237,8 @@
 	/// @dev EVM selector for this function is: 0xd34b55b8,
 	///  or in textual repr: uniqueCollectionType()
 	function uniqueCollectionType() external returns (string memory);
-
-	/// Changes collection owner to another account
-	///
-	/// @dev Owner can be changed only by current owner
-	/// @param newOwner new owner account
-	/// @dev EVM selector for this function is: 0x13af4035,
-	///  or in textual repr: setOwner(address)
-	function setOwner(address newOwner) external;
-
-	/// Changes collection owner to another substrate account
-	///
-	/// @dev Owner can be changed only by current owner
-	/// @param newOwner new owner substrate account
-	/// @dev EVM selector for this function is: 0xb212138f,
-	///  or in textual repr: setOwnerSubstrate(uint256)
-	function setOwnerSubstrate(uint256 newOwner) external;
 }
 
-/// @dev anonymous struct
-struct Tuple6 {
-	address field_0;
-	uint256 field_1;
-}
-
 /// @dev the ERC-165 identifier for this interface is 0x79cc6790
 interface ERC20UniqueExtensions is Dummy, ERC165 {
 	/// @dev EVM selector for this function is: 0x79cc6790,
@@ -298,6 +306,7 @@
 	Dummy,
 	ERC165,
 	ERC20,
+	ERC20Mintable,
 	ERC20UniqueExtensions,
 	Collection
 {}
modifiedtests/src/eth/fungible.test.tsdiffbeforeafterboth
14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
1616
17import {approveExpectSuccess, createCollectionExpectSuccess, createFungibleItemExpectSuccess, transferExpectSuccess, transferFromExpectSuccess, UNIQUE} from '../util/helpers';17import {approveExpectSuccess, createCollection, createCollectionExpectSuccess, createFungibleItemExpectSuccess, transferExpectSuccess, transferFromExpectSuccess, UNIQUE} from '../util/helpers';
18import {collectionIdToAddress, createEthAccount, createEthAccountWithBalance, GAS_ARGS, itWeb3, normalizeEvents, recordEthFee, recordEvents, subToEth, transferBalanceToEth} from './util/helpers';18import {collectionIdToAddress, createEthAccount, createEthAccountWithBalance, evmCollection, GAS_ARGS, itWeb3, normalizeEvents, recordEthFee, recordEvents, subToEth, transferBalanceToEth} from './util/helpers';
19import fungibleAbi from './fungibleAbi.json';19import fungibleAbi from './fungibleAbi.json';
20import {expect} from 'chai';20import {expect} from 'chai';
21import {submitTransactionAsync} from '../substrate/substrate-api';
2122
22describe('Fungible: Information getting', () => {23describe('Fungible: Information getting', () => {
23 itWeb3('totalSupply', async ({api, web3, privateKeyWrapper}) => {24 itWeb3('totalSupply', async ({api, web3, privateKeyWrapper}) => {
58});59});
5960
60describe('Fungible: Plain calls', () => {61describe('Fungible: Plain calls', () => {
62 itWeb3('Can perform mint()', async ({web3, api, privateKeyWrapper}) => {
63 const alice = privateKeyWrapper('//Alice');
64 const collection = await createCollection(api, alice, {
65 name: 'token name',
66 mode: {type: 'Fungible', decimalPoints: 0},
67 });
68
69 const receiver = createEthAccount(web3);
70
71 const collectionIdAddress = collectionIdToAddress(collection.collectionId);
72 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
73 const changeAdminTx = api.tx.unique.addCollectionAdmin(collection.collectionId, {Ethereum: owner});
74 await submitTransactionAsync(alice, changeAdminTx);
75
76 const collectionContract = evmCollection(web3, owner, collectionIdAddress, {type: 'Fungible', decimalPoints: 0});
77 const result = await collectionContract.methods.mint(receiver, 100).send();
78 const events = normalizeEvents(result.events);
79
80 expect(events).to.be.deep.equal([
81 {
82 address: collectionIdAddress,
83 event: 'Transfer',
84 args: {
85 from: '0x0000000000000000000000000000000000000000',
86 to: receiver,
87 value: '100',
88 },
89 },
90 ]);
91 });
92
93 itWeb3('Can perform mintBulk()', async ({web3, api, privateKeyWrapper}) => {
94 const alice = privateKeyWrapper('//Alice');
95 const collection = await createCollection(api, alice, {
96 name: 'token name',
97 mode: {type: 'Fungible', decimalPoints: 0},
98 });
99
100 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
101 const receiver1 = createEthAccount(web3);
102 const receiver2 = createEthAccount(web3);
103 const receiver3 = createEthAccount(web3);
104
105 const collectionIdAddress = collectionIdToAddress(collection.collectionId);
106 const changeAdminTx = api.tx.unique.addCollectionAdmin(collection.collectionId, {Ethereum: owner});
107 await submitTransactionAsync(alice, changeAdminTx);
108
109 const collectionContract = evmCollection(web3, owner, collectionIdAddress, {type: 'Fungible', decimalPoints: 0});
110 const result = await collectionContract.methods.mintBulk([
111 [receiver1, 10],
112 [receiver2, 20],
113 [receiver3, 30],
114 ]).call();
115 console.log(result);
116 const events = normalizeEvents(result.events);
117
118 expect(events).to.be.deep.equal([
119 {
120 address:collectionIdAddress,
121 event: 'Transfer',
122 args: {
123 from: '0x0000000000000000000000000000000000000000',
124 to: receiver1,
125 value: '10',
126 },
127 },
128 {
129 address:collectionIdAddress,
130 event: 'Transfer',
131 args: {
132 from: '0x0000000000000000000000000000000000000000',
133 to: receiver2,
134 value: '20',
135 },
136 },
137 {
138 address:collectionIdAddress,
139 event: 'Transfer',
140 args: {
141 from: '0x0000000000000000000000000000000000000000',
142 to: receiver3,
143 value: '30',
144 },
145 },
146 ]);
147 });
148
149 itWeb3('Can perform burn()', async ({web3, api, privateKeyWrapper}) => {
150 const alice = privateKeyWrapper('//Alice');
151 const collection = await createCollection(api, alice, {
152 name: 'token name',
153 mode: {type: 'Fungible', decimalPoints: 0},
154 });
155
156 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
157 const changeAdminTx = api.tx.unique.addCollectionAdmin(collection.collectionId, {Ethereum: owner});
158 await submitTransactionAsync(alice, changeAdminTx);
159 const receiver = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
160
161 const collectionIdAddress = collectionIdToAddress(collection.collectionId);
162 const collectionContract = evmCollection(web3, owner, collectionIdAddress, {type: 'Fungible', decimalPoints: 0});
163 await collectionContract.methods.mint(receiver, 100).send();
164
165 const result = await collectionContract.methods.burnFrom(receiver, 49).send({from: receiver});
166
167 const events = normalizeEvents(result.events);
168
169 expect(events).to.be.deep.equal([
170 {
171 address: collectionIdAddress,
172 event: 'Transfer',
173 args: {
174 from: receiver,
175 to: '0x0000000000000000000000000000000000000000',
176 value: '49',
177 },
178 },
179 ]);
180
181 const balance = await collectionContract.methods.balanceOf(receiver).call();
182 expect(balance).to.equal('51');
183 });
184
61 itWeb3('Can perform approve()', async ({web3, api, privateKeyWrapper}) => {185 itWeb3('Can perform approve()', async ({web3, api, privateKeyWrapper}) => {
62 const collection = await createCollectionExpectSuccess({186 const collection = await createCollectionExpectSuccess({
modifiedtests/src/eth/fungibleAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/fungibleAbi.json
+++ b/tests/src/eth/fungibleAbi.json
@@ -151,6 +151,33 @@
     "type": "function"
   },
   {
+    "inputs": [
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "mint",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "field_0", "type": "address" },
+          { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+        ],
+        "internalType": "struct Tuple0[]",
+        "name": "amounts",
+        "type": "tuple[]"
+      }
+    ],
+    "name": "mintBulk",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
     "inputs": [],
     "name": "getCollectionSponsor",
     "outputs": [