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
129 }129 }
130}130}
131
132#[solidity_interface(name = "ERC20Mintable")]
133impl<T: Config> FungibleHandle<T> {
134 #[weight(<SelfWeightOf<T>>::create_item())]
135 fn mint(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {
136 let caller = T::CrossAccountId::from_eth(caller);
137 let to = T::CrossAccountId::from_eth(to);
138 let amount = amount.try_into().map_err(|_| "amount overflow")?;
139 let budget = self
140 .recorder
141 .weight_calls_budget(<StructureWeight<T>>::find_parent());
142 <Pallet<T>>::create_item(&self, &caller, (to, amount), &budget)
143 .map_err(dispatch_to_evm::<T>)?;
144 Ok(true)
145 }
146}
131147
132#[solidity_interface(name = ERC20UniqueExtensions)]148#[solidity_interface(name = "ERC20UniqueExtensions")]
133impl<T: Config> FungibleHandle<T> {149impl<T: Config> FungibleHandle<T> {
134 #[weight(<SelfWeightOf<T>>::burn_from())]150 #[weight(<SelfWeightOf<T>>::burn_from())]
135 fn burn_from(&mut self, caller: caller, from: address, amount: uint256) -> Result<bool> {151 fn burn_from(&mut self, caller: caller, from: address, amount: uint256) -> Result<bool> {
145 Ok(true)161 Ok(true)
146 }162 }
163
164 #[weight(<SelfWeightOf<T>>::create_multiple_items_ex(amounts.len() as u32))]
165 fn mint_bulk(&mut self, caller: caller, amounts: Vec<(address, uint256)>) -> Result<bool> {
166 let caller = T::CrossAccountId::from_eth(caller);
167 let budget = self
168 .recorder
169 .weight_calls_budget(<StructureWeight<T>>::find_parent());
170 let amounts = amounts
171 .into_iter()
172 .map(|(to, amount)| Ok((T::CrossAccountId::from_eth(to), amount.try_into().map_err(|_| "amount overflow")?)))
173 .collect::<Result<_>>()?;
174
175 <Pallet<T>>::create_multiple_items(&self, &caller, amounts, &budget)
176 .map_err(dispatch_to_evm::<T>)?;
177 Ok(true)
178 }
147}179}
148180
149#[solidity_interface(181#[solidity_interface(
150 name = UniqueFungible,182 name = UniqueFungible,
151 is(183 is(
152 ERC20,184 ERC20,
185 ERC20Mintable,
153 ERC20UniqueExtensions,186 ERC20UniqueExtensions,
154 Collection(common_mut, CollectionHandle<T>),187 Collection(common_mut, CollectionHandle<T>),
155 )188 )
modifiedpallets/fungible/src/stubs/UniqueFungible.soldiffbeforeafterboth
33
4pragma solidity >=0.8.0 <0.9.0;4pragma solidity >=0.8.0 <0.9.0;
5
6// Anonymous struct
7struct Tuple0 {
8 address field_0;
9 uint256 field_1;
10}
511
6/// @dev common stubs holder12// Common stubs holder
7contract Dummy {13contract Dummy {
8 uint8 dummy;14 uint8 dummy;
9 string stub_error = "this contract is implemented in native";15 string stub_error = "this contract is implemented in native";
21 }27 }
22}28}
2329
24/// @title A contract that allows you to work with collections.30// Inline
25/// @dev the ERC-165 identifier for this interface is 0xe54be64031contract ERC20Events {
32 event Transfer(address indexed from, address indexed to, uint256 value);
33 event Approval(
34 address indexed owner,
35 address indexed spender,
36 uint256 value
37 );
38}
39
40// Selector: 40c10f19
41contract ERC20Mintable is Dummy, ERC165 {
42 // Selector: mint(address,uint256) 40c10f19
43 function mint(address to, uint256 amount) public returns (bool) {
44 require(false, stub_error);
45 to;
46 amount;
47 dummy = 0;
48 return false;
49 }
50}
51
52// Selector: 63034ac5
53contract ERC20UniqueExtensions is Dummy, ERC165 {
54 // Selector: burnFrom(address,uint256) 79cc6790
55 function burnFrom(address from, uint256 amount) public returns (bool) {
56 require(false, stub_error);
57 from;
58 amount;
59 dummy = 0;
60 return false;
61 }
62
63 // Selector: mintBulk((address,uint256)[]) 1acf2d55
64 function mintBulk(Tuple0[] memory amounts) public returns (bool) {
65 require(false, stub_error);
66 amounts;
67 dummy = 0;
68 return false;
69 }
70}
71
72// Selector: 6cf113cd
26contract Collection is Dummy, ERC165 {73contract Collection is Dummy, ERC165 {
27 /// Set collection property.74 /// Set collection property.
28 ///75 ///
476 Dummy,523 Dummy,
477 ERC165,524 ERC165,
478 ERC20,525 ERC20,
526 ERC20Mintable,
479 ERC20UniqueExtensions,527 ERC20UniqueExtensions,
480 Collection528 Collection
481{}529{}
modifiedtests/src/eth/api/UniqueFungible.soldiffbeforeafterboth
33
4pragma solidity >=0.8.0 <0.9.0;4pragma solidity >=0.8.0 <0.9.0;
5
6// Anonymous struct
7struct Tuple0 {
8 address field_0;
9 uint256 field_1;
10}
511
6/// @dev common stubs holder12// Common stubs holder
7interface Dummy {13interface Dummy {
814
9}15}
12 function supportsInterface(bytes4 interfaceID) external view returns (bool);18 function supportsInterface(bytes4 interfaceID) external view returns (bool);
13}19}
1420
15/// @title A contract that allows you to work with collections.21// Inline
16/// @dev the ERC-165 identifier for this interface is 0xe54be64022interface ERC20Events {
23 event Transfer(address indexed from, address indexed to, uint256 value);
24 event Approval(
25 address indexed owner,
26 address indexed spender,
27 uint256 value
28 );
29}
30
31// Selector: 40c10f19
32interface ERC20Mintable is Dummy, ERC165 {
33 // Selector: mint(address,uint256) 40c10f19
34 function mint(address to, uint256 amount) external returns (bool);
35}
36
37// Selector: 63034ac5
38interface ERC20UniqueExtensions is Dummy, ERC165 {
39 // Selector: burnFrom(address,uint256) 79cc6790
40 function burnFrom(address from, uint256 amount) external returns (bool);
41
42 // Selector: mintBulk((address,uint256)[]) 1acf2d55
43 function mintBulk(Tuple0[] memory amounts) external returns (bool);
44}
45
46// Selector: 6cf113cd
17interface Collection is Dummy, ERC165 {47interface Collection is Dummy, ERC165 {
18 /// Set collection property.48 /// Set collection property.
19 ///49 ///
208 /// or in textual repr: uniqueCollectionType()238 /// or in textual repr: uniqueCollectionType()
209 function uniqueCollectionType() external returns (string memory);239 function uniqueCollectionType() external returns (string memory);
210
211 /// Changes collection owner to another account
212 ///
213 /// @dev Owner can be changed only by current owner
214 /// @param newOwner new owner account
215 /// @dev EVM selector for this function is: 0x13af4035,
216 /// or in textual repr: setOwner(address)
217 function setOwner(address newOwner) external;
218
219 /// Changes collection owner to another substrate account
220 ///
221 /// @dev Owner can be changed only by current owner
222 /// @param newOwner new owner substrate account
223 /// @dev EVM selector for this function is: 0xb212138f,
224 /// or in textual repr: setOwnerSubstrate(uint256)
225 function setOwnerSubstrate(uint256 newOwner) external;
226}240}
227
228/// @dev anonymous struct
229struct Tuple6 {
230 address field_0;
231 uint256 field_1;
232}
233241
234/// @dev the ERC-165 identifier for this interface is 0x79cc6790242/// @dev the ERC-165 identifier for this interface is 0x79cc6790
235interface ERC20UniqueExtensions is Dummy, ERC165 {243interface ERC20UniqueExtensions is Dummy, ERC165 {
298 Dummy,306 Dummy,
299 ERC165,307 ERC165,
300 ERC20,308 ERC20,
309 ERC20Mintable,
301 ERC20UniqueExtensions,310 ERC20UniqueExtensions,
302 Collection311 Collection
303{}312{}
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
150 "stateMutability": "nonpayable",150 "stateMutability": "nonpayable",
151 "type": "function"151 "type": "function"
152 },152 },
153 {
154 "inputs": [
155 { "internalType": "address", "name": "to", "type": "address" },
156 { "internalType": "uint256", "name": "amount", "type": "uint256" }
157 ],
158 "name": "mint",
159 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
160 "stateMutability": "nonpayable",
161 "type": "function"
162 },
163 {
164 "inputs": [
165 {
166 "components": [
167 { "internalType": "address", "name": "field_0", "type": "address" },
168 { "internalType": "uint256", "name": "field_1", "type": "uint256" }
169 ],
170 "internalType": "struct Tuple0[]",
171 "name": "amounts",
172 "type": "tuple[]"
173 }
174 ],
175 "name": "mintBulk",
176 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
177 "stateMutability": "nonpayable",
178 "type": "function"
179 },
153 {180 {
154 "inputs": [],181 "inputs": [],
155 "name": "getCollectionSponsor",182 "name": "getCollectionSponsor",