git.delta.rocks / unique-network / refs/commits / 49182bd0a7fd

difftreelog

Merge pull request #788 from UniqueNetwork/tests/eth-helpers

ut-akuznetsov2022-12-19parents: #c12162a #bb64e2b.patch.diff
in: master

60 files changed

addedtests/src/.outdated/rmrk/acceptNft.seqtest.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/rmrk/addResource.seqtest.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/rmrk/addTheme.seqtest.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/rmrk/burnNft.seqtest.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/rmrk/changeCollectionIssuer.seqtest.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/rmrk/createBase.seqtest.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/rmrk/createCollection.seqtest.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/rmrk/deleteCollection.seqtest.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/rmrk/equipNft.seqtest.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/rmrk/getOwnedNfts.seqtest.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/rmrk/lockCollection.seqtest.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/rmrk/mintNft.seqtest.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/rmrk/rejectNft.seqtest.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/rmrk/removeResource.seqtest.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/rmrk/rmrkIsolation.seqtest.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/rmrk/sendNft.seqtest.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/rmrk/setCollectionProperty.seqtest.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/rmrk/setEquippableList.seqtest.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/rmrk/setNftProperty.seqtest.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/rmrk/setResourcePriorities.seqtest.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/rmrk/util/fetch.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/rmrk/util/helpers.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/rmrk/util/tx.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/substrate/get-balance.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/substrate/privateKey.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/substrate/promisify-substrate.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/substrate/substrate-api.tsdiffbeforeafterboth

no changes

addedtests/src/.outdated/substrate/wait-new-blocks.tsdiffbeforeafterboth

no changes

modifiedtests/src/calibrateApply.tsdiffbeforeafterboth
1import {readFile, writeFile} from 'fs/promises';1import {readFile, writeFile} from 'fs/promises';
2import path from 'path';2import path from 'path';
3import usingApi from './substrate/substrate-api';3import usingApi from './.outdated/substrate/substrate-api';
44
5const formatNumber = (num: string): string => num.split('').reverse().join('').replace(/([0-9]{3})/g, '$1_').split('').reverse().join('').replace(/^_/, '');5const formatNumber = (num: string): string => num.split('').reverse().join('').replace(/([0-9]{3})/g, '$1_').split('').reverse().join('').replace(/^_/, '');
66
modifiedtests/src/eth/allowlist.test.tsdiffbeforeafterboth
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 {IKeyringPair} from '@polkadot/types/types';17import {IKeyringPair} from '@polkadot/types/types';
18import {Pallets} from '../util';
18import {itEth, usingEthPlaygrounds, expect} from './util';19import {itEth, usingEthPlaygrounds, expect, SponsoringMode} from './util';
1920
20describe('EVM contract allowlist', () => {21describe('EVM contract allowlist', () => {
21 let donor: IKeyringPair;22 let donor: IKeyringPair;
92 });93 });
9394
95
96 [
97 {mode: 'nft' as const, requiredPallets: []},
98 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
99 {mode: 'ft' as const, requiredPallets: []},
100 ].map(testCase =>
94 itEth('Collection allowlist can be added and removed by [cross] address', async ({helper}) => {101 itEth.ifWithPallets(`Collection allowlist can be added and removed by [cross] address for ${testCase.mode}`, testCase.requiredPallets, async ({helper}) => {
95 const owner = (await helper.eth.createAccountWithBalance(donor)).toLowerCase();102 const owner = (await helper.eth.createAccountWithBalance(donor)).toLowerCase();
96 const [userSub] = await helper.arrange.createAccounts([10n], donor);103 const [userSub] = await helper.arrange.createAccounts([10n], donor);
97 const userEth = await helper.eth.createAccountWithBalance(donor);104 const userEth = await helper.eth.createAccountWithBalance(donor);
98 105 const mintParams = testCase.mode === 'ft' ? [userEth, 100] : [userEth];
106
99 const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');107 const {collectionAddress, collectionId} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');
100 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);108 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
101 const userCrossSub = helper.ethCrossAccount.fromKeyringPair(userSub);109 const userCrossSub = helper.ethCrossAccount.fromKeyringPair(userSub);
102 const userCrossEth = helper.ethCrossAccount.fromAddress(userEth);110 const userCrossEth = helper.ethCrossAccount.fromAddress(userEth);
103 const ownerCrossEth = helper.ethCrossAccount.fromAddress(owner);111 const ownerCrossEth = helper.ethCrossAccount.fromAddress(owner);
108 await collectionEvm.methods.addToCollectionAllowListCross(userCrossEth).send({from: owner});116 await collectionEvm.methods.addToCollectionAllowListCross(userCrossEth).send({from: owner});
109 await collectionEvm.methods.addToCollectionAllowListCross(ownerCrossEth).send({from: owner});117 await collectionEvm.methods.addToCollectionAllowListCross(ownerCrossEth).send({from: owner});
118
119 // Accounts are in allowed list:
110 expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.true;120 expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.true;
111 expect(await helper.collection.allowed(collectionId, {Ethereum: userEth})).to.be.true;121 expect(await helper.collection.allowed(collectionId, {Ethereum: userEth})).to.be.true;
112 expect(await collectionEvm.methods.allowlistedCross(userCrossSub).call({from: owner})).to.be.true;122 expect(await collectionEvm.methods.allowlistedCross(userCrossSub).call({from: owner})).to.be.true;
113 expect(await collectionEvm.methods.allowlistedCross(userCrossEth).call({from: owner})).to.be.true;123 expect(await collectionEvm.methods.allowlistedCross(userCrossEth).call({from: owner})).to.be.true;
114124
115 await collectionEvm.methods.mint(userEth).send(); // token #1125 await collectionEvm.methods.mint(...mintParams).send({from: owner}); // token #1
116 await collectionEvm.methods.mint(userEth).send(); // token #2126 await collectionEvm.methods.mint(...mintParams).send({from: owner}); // token #2
117 await collectionEvm.methods.setCollectionAccess(1).send();127 await collectionEvm.methods.setCollectionAccess(SponsoringMode.Allowlisted).send({from: owner});
118 128
119 // allowlisted account can transfer and transferCross:129 // allowlisted account can transfer and transferCross from eth:
120 await collectionEvm.methods.transfer(owner, 1).send({from: userEth});130 await collectionEvm.methods.transfer(owner, 1).send({from: userEth});
121 await collectionEvm.methods.transferCross(userCrossSub, 2).send({from: userEth});131 await collectionEvm.methods.transferCross(userCrossSub, 2).send({from: userEth});
132
133 if (testCase.mode === 'ft') {
134 expect(await helper.ft.getBalance(collectionId, {Ethereum: owner})).to.eq(1n);
135 expect(await helper.ft.getBalance(collectionId, {Substrate: userSub.address})).to.eq(2n);
136 } else {
122 expect(await helper.nft.getTokenOwner(collectionId, 1)).to.deep.eq({Ethereum: owner});137 expect(await helper.nft.getTokenOwner(collectionId, 1)).to.deep.eq({Ethereum: owner});
123 expect(await helper.nft.getTokenOwner(collectionId, 2)).to.deep.eq({Substrate: userSub.address});138 expect(await helper.nft.getTokenOwner(collectionId, 2)).to.deep.eq({Substrate: userSub.address});
124 139 }
140
141 // allowlisted cross substrate accounts can transfer from Substrate:
142 testCase.mode === 'ft'
143 ? await helper.ft.transfer(userSub, collectionId, {Ethereum: userEth}, 2n)
144 : await helper.collection.transferToken(userSub, collectionId, 2, {Ethereum: userEth});
145
125 // can removeFromCollectionAllowListCross:146 // can removeFromCollectionAllowListCross:
126 await collectionEvm.methods.removeFromCollectionAllowListCross(userCrossSub).send({from: owner});147 await collectionEvm.methods.removeFromCollectionAllowListCross(userCrossSub).send({from: owner});
131 expect(await collectionEvm.methods.allowlistedCross(userCrossEth).call({from: owner})).to.be.false;152 expect(await collectionEvm.methods.allowlistedCross(userCrossEth).call({from: owner})).to.be.false;
132153
133 // cannot transfer anymore154 // cannot transfer anymore
134 await collectionEvm.methods.mint(userEth).send();155 await collectionEvm.methods.mint(...mintParams).send({from: owner});
135 await expect(collectionEvm.methods.transfer(owner, 2).send({from: userEth})).to.be.rejectedWith(/Transaction has been reverted/);156 await expect(collectionEvm.methods.transfer(owner, 2).send({from: userEth})).to.be.rejectedWith(/Transaction has been reverted/);
136 });157 }));
137158
159 [
160 // cross-methods
161 {mode: 'nft' as const, cross: true, requiredPallets: []},
162 {mode: 'rft' as const, cross: true, requiredPallets: [Pallets.ReFungible]},
163 {mode: 'ft' as const, cross: true, requiredPallets: []},
138 // Soft-deprecated164 // soft-deprecated
165 {mode: 'nft' as const, cross: false, requiredPallets: []},
166 {mode: 'rft' as const, cross: false, requiredPallets: [Pallets.ReFungible]},
167 {mode: 'ft' as const, cross: false, requiredPallets: []},
168 ].map(testCase =>
139 itEth('Collection allowlist can not be add and remove [eth] address by not owner', async ({helper}) => {169 itEth.ifWithPallets(`Non-owner cannot add or remove from collection allowlist ${testCase.cross ? 'cross ' : ''}${testCase.mode}`, testCase.requiredPallets, async ({helper}) => {
170 // Select methods:
171 const addToAllowList = testCase.cross ? 'addToCollectionAllowListCross' : 'addToCollectionAllowList';
172 const removeFromAllowList = testCase.cross ? 'removeFromCollectionAllowListCross' : 'removeFromCollectionAllowList';
173
140 const owner = await helper.eth.createAccountWithBalance(donor);174 const owner = await helper.eth.createAccountWithBalance(donor);
141 const notOwner = await helper.eth.createAccountWithBalance(donor);175 const notOwner = await helper.eth.createAccountWithBalance(donor);
176 const userSub = donor;
177 const userCrossSub = helper.ethCrossAccount.fromKeyringPair(userSub);
142 const user = helper.eth.createAccount();178 const userEth = helper.eth.createAccount();
143 const crossUser = helper.ethCrossAccount.fromAddress(user);179 const userCrossEth = helper.ethCrossAccount.fromAddress(userEth);
144180
145 const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');181 const {collectionAddress, collectionId} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');
146 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);182 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner, !testCase.cross);
147183
148 expect(await collectionEvm.methods.allowlistedCross(crossUser).call({from: owner})).to.be.false;184 expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.false;
185 expect(await helper.collection.allowed(collectionId, {Ethereum: userEth})).to.be.false;
186
187 // 1. notOwner cannot add to allow list:
188 // 1.1 plain ethereum or cross address:
149 await expect(collectionEvm.methods.addToCollectionAllowList(user).call({from: notOwner})).to.be.rejectedWith('NoPermission');189 await expect(collectionEvm.methods[addToAllowList](testCase.cross ? userCrossEth : userEth).call({from: notOwner})).to.be.rejectedWith('NoPermission');
190 // 1.2 cross-substrate address:
191 if (testCase.cross)
150 expect(await collectionEvm.methods.allowlistedCross(crossUser).call({from: owner})).to.be.false;192 await expect(collectionEvm.methods[addToAllowList](userCrossSub).call({from: notOwner})).to.be.rejectedWith('NoPermission');
193
194 // 2. owner can add to allow list:
195 // 2.1 plain ethereum or cross address:
151 await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner});196 await collectionEvm.methods[addToAllowList](testCase.cross ? userCrossEth : userEth).send({from: owner});
197 // 2.2 cross-substrate address:
198 if (testCase.cross) {
152 expect(await collectionEvm.methods.allowlistedCross(crossUser).call({from: owner})).to.be.true;199 await collectionEvm.methods[addToAllowList](userCrossSub).send({from: owner});
200 expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.true;
201 }
202 expect(await helper.collection.allowed(collectionId, {Ethereum: userEth})).to.be.true;
203
204 // 3. notOwner cannot remove from allow list:
205 // 3.1 plain ethereum or cross address:
153 await expect(collectionEvm.methods.removeFromCollectionAllowList(user).call({from: notOwner})).to.be.rejectedWith('NoPermission');206 await expect(collectionEvm.methods[removeFromAllowList](testCase.cross ? userCrossEth : userEth).call({from: notOwner})).to.be.rejectedWith('NoPermission');
207 // 3.2 cross-substrate address:
208 if (testCase.cross)
154 expect(await collectionEvm.methods.allowlistedCross(crossUser).call({from: owner})).to.be.true;209 await expect(collectionEvm.methods[removeFromAllowList](userCrossSub).call({from: notOwner})).to.be.rejectedWith('NoPermission');
155 });210 }));
156
157 itEth('Collection allowlist can not be add and remove [cross] address by not owner', async ({helper}) => {
158 const owner = await helper.eth.createAccountWithBalance(donor);
159 const notOwner = await helper.eth.createAccountWithBalance(donor);
160 const user = donor;
161
162 const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
163 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
164
165 expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;
166 const userCross = helper.ethCrossAccount.fromKeyringPair(user);
167 await expect(collectionEvm.methods.addToCollectionAllowListCross(userCross).call({from: notOwner})).to.be.rejectedWith('NoPermission');
168 expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;
169 await collectionEvm.methods.addToCollectionAllowListCross(userCross).send({from: owner});
170
171 expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.true;
172 await expect(collectionEvm.methods.removeFromCollectionAllowListCross(userCross).call({from: notOwner})).to.be.rejectedWith('NoPermission');
173 expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.true;
174 });
175});211});
176212
modifiedtests/src/eth/reFungibleToken.test.tsdiffbeforeafterboth
208 }208 }
209 });209 });
210 210
211 itEth('Can perform transferFrom()', async ({helper}) => {211 [
212 'transferFrom',
213 'transferFromCross',
214 ].map(testCase =>
215 itEth(`Can perform ${testCase}`, async ({helper}) => {
212 const owner = await helper.eth.createAccountWithBalance(donor);216 const isCross = testCase === 'transferFromCross';
217 const owner = await helper.eth.createAccountWithBalance(donor);
213 const spender = await helper.eth.createAccountWithBalance(donor);218 const ownerCross = helper.ethCrossAccount.fromAddress(owner);
219 const spender = await helper.eth.createAccountWithBalance(donor);
214 const receiver = helper.eth.createAccount();220 const receiverEth = helper.eth.createAccount();
215 const collection = await helper.rft.mintCollection(alice);221 const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
222 const [receiverSub] = await helper.arrange.createAccounts([1n], donor);
223 const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);
224
225 const collection = await helper.rft.mintCollection(alice);
216 const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});226 const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
217227
218 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);228 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
219 const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);229 const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);
220230
221 await contract.methods.approve(spender, 100).send();231 await contract.methods.approve(spender, 100).send({from: owner});
232
233 // 1. Can transfer from
234 // 1.1 Plain ethereum or cross address:
235 {
236 const result = await contract.methods[testCase](
237 isCross ? ownerCross : owner,
238 isCross ? receiverCrossEth : receiverEth,
239 49,
240 ).send({from: spender});
222241
223 {242 // Check events:
224 const result = await contract.methods.transferFrom(owner, receiver, 49).send({from: spender});
225 let event = result.events.Transfer;243 const transferEvent = result.events.Transfer;
226 expect(event.address).to.be.equal(tokenAddress);244 expect(transferEvent.address).to.be.equal(tokenAddress);
227 expect(event.returnValues.from).to.be.equal(owner);245 expect(transferEvent.returnValues.from).to.be.equal(owner);
228 expect(event.returnValues.to).to.be.equal(receiver);246 expect(transferEvent.returnValues.to).to.be.equal(receiverEth);
229 expect(event.returnValues.value).to.be.equal('49');247 expect(transferEvent.returnValues.value).to.be.equal('49');
230248
231 event = result.events.Approval;249 const approvalEvent = result.events.Approval;
232 expect(event.address).to.be.equal(tokenAddress);250 expect(approvalEvent.address).to.be.equal(tokenAddress);
233 expect(event.returnValues.owner).to.be.equal(owner);251 expect(approvalEvent.returnValues.owner).to.be.equal(owner);
234 expect(event.returnValues.spender).to.be.equal(spender);252 expect(approvalEvent.returnValues.spender).to.be.equal(spender);
235 expect(event.returnValues.value).to.be.equal('51');253 expect(approvalEvent.returnValues.value).to.be.equal('51');
236 }
237254
238 {255 // Check balances:
239 const balance = await contract.methods.balanceOf(receiver).call();256 const receiverBalance = await contract.methods.balanceOf(receiverEth).call();
240 expect(+balance).to.equal(49);257 const ownerBalance = await contract.methods.balanceOf(owner).call();
241 }
242258
243 {259 expect(+receiverBalance).to.equal(49);
244 const balance = await contract.methods.balanceOf(owner).call();
245 expect(+balance).to.equal(151);
246 }260 expect(+ownerBalance).to.equal(151);
247 });
248
249 itEth('Can perform transferFromCross()', async ({helper}) => {
250 const owner = await helper.eth.createAccountWithBalance(donor);
251 const ownerCross = helper.ethCrossAccount.fromAddress(owner);261 }
252 const spender = await helper.eth.createAccountWithBalance(donor);
253 const receiver = helper.eth.createAccount();
254 const receiverSub = (await helper.arrange.createAccounts([1n], donor))[0];
255 const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiver);
256 const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);
257 262
258 const collection = await helper.rft.mintCollection(alice);263 // 1.2 Cross substrate address:
264 if (testCase === 'transferFromCross') {
265 const result = await contract.methods.transferFromCross(ownerCross, receiverCrossSub, 51).send({from: spender});
259 const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});266 // Check events:
267 const transferEvent = result.events.Transfer;
268 expect(transferEvent.address).to.be.equal(tokenAddress);
269 expect(transferEvent.returnValues.from).to.be.equal(owner);
270 expect(transferEvent.returnValues.to).to.be.equal(helper.address.substrateToEth(receiverSub.address));
271 expect(transferEvent.returnValues.value).to.be.equal('51');
260272
261 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);273 const approvalEvent = result.events.Approval;
274 expect(approvalEvent.address).to.be.equal(tokenAddress);
275 expect(approvalEvent.returnValues.owner).to.be.equal(owner);
262 const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);276 expect(approvalEvent.returnValues.spender).to.be.equal(spender);
277 expect(approvalEvent.returnValues.value).to.be.equal('0');
263278
264 await contract.methods.approve(spender, 100).send({from: owner});279 // Check balances:
265
266 {280 const receiverBalance = await collection.getTokenBalance(tokenId, {Substrate: receiverSub.address});
267 const result = await contract.methods.transferFromCross(ownerCross, receiverCrossEth, 49).send({from: spender});
268 let event = result.events.Transfer;281 const ownerBalance = await contract.methods.balanceOf(owner).call();
269 expect(event.address).to.be.equal(tokenAddress);
270 expect(event.returnValues.from).to.be.equal(owner);282 expect(receiverBalance).to.equal(51n);
271 expect(event.returnValues.to).to.be.equal(receiver);283 expect(+ownerBalance).to.equal(100);
272 expect(event.returnValues.value).to.be.equal('49');284 }
285 }));
273286
274 event = result.events.Approval;287 [
288 'transfer',
289 'transferCross',
290 ].map(testCase =>
275 expect(event.address).to.be.equal(tokenAddress);291 itEth(`Can perform ${testCase}()`, async ({helper}) => {
292 const isCross = testCase === 'transferCross';
276 expect(event.returnValues.owner).to.be.equal(owner);293 const owner = await helper.eth.createAccountWithBalance(donor);
294 const receiverEth = helper.eth.createAccount();
277 expect(event.returnValues.spender).to.be.equal(spender);295 const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
296 const [receiverSub] = await helper.arrange.createAccounts([1n], donor);
278 expect(event.returnValues.value).to.be.equal('51');297 const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);
298 const collection = await helper.rft.mintCollection(alice);
279 }299 const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
280300
281 {301 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
282 const balance = await contract.methods.balanceOf(receiver).call();
283 expect(+balance).to.equal(49);302 const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);
284 }
285303
286 {304 // 1. Can transfer to plain ethereum or cross-ethereum account:
305 {
287 const balance = await contract.methods.balanceOf(owner).call();306 const result = await contract.methods[testCase](isCross ? receiverCrossEth : receiverEth, 50).send({from: owner});
307 // Check events
308 const transferEvent = result.events.Transfer;
309 expect(transferEvent.address).to.be.equal(tokenAddress);
310 expect(transferEvent.returnValues.from).to.be.equal(owner);
311 expect(transferEvent.returnValues.to).to.be.equal(receiverEth);
312 expect(transferEvent.returnValues.value).to.be.equal('50');
313 // Check balances:
314 const ownerBalance = await contract.methods.balanceOf(owner).call();
288 expect(+balance).to.equal(151);315 const receiverBalance = await contract.methods.balanceOf(receiverEth).call();
316 expect(+ownerBalance).to.equal(150);
317 expect(+receiverBalance).to.equal(50);
289 }318 }
290 319
291 {320 // 2. Can transfer to cross-substrate account:
321 if(isCross) {
292 const result = await contract.methods.transferFromCross(ownerCross, receiverCrossSub, 51).send({from: spender});322 const result = await contract.methods.transferCross(receiverCrossSub, 50).send({from: owner});
293 let event = result.events.Transfer;323 // Check events:
324 const event = result.events.Transfer;
294 expect(event.address).to.be.equal(tokenAddress);325 expect(event.address).to.be.equal(tokenAddress);
295 expect(event.returnValues.from).to.be.equal(owner);326 expect(event.returnValues.from).to.be.equal(owner);
296 expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(receiverSub.address));327 expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(receiverSub.address));
297 expect(event.returnValues.value).to.be.equal('51');328 expect(event.returnValues.value).to.be.equal('50');
329 // Check balances:
330 const ownerBalance = await contract.methods.balanceOf(owner).call();
331 const receiverBalance = await collection.getTokenBalance(tokenId, {Substrate: receiverSub.address});
332 expect(+ownerBalance).to.equal(100);
333 expect(receiverBalance).to.equal(50n);
334 }
335 }));
298336
299 event = result.events.Approval;
300 expect(event.address).to.be.equal(tokenAddress);
301 expect(event.returnValues.owner).to.be.equal(owner);
302 expect(event.returnValues.spender).to.be.equal(spender);
303 expect(event.returnValues.value).to.be.equal('0');
304 }
305
306 {
307 const balance = await collection.getTokenBalance(tokenId, {Substrate: receiverSub.address});
308 expect(balance).to.equal(51n);
309 }
310
311 {
312 const balance = await contract.methods.balanceOf(owner).call();
313 expect(+balance).to.equal(100);
314 }
315 });
316
317 itEth('Can perform transfer()', async ({helper}) => {
318 const owner = await helper.eth.createAccountWithBalance(donor);
319 const receiver = helper.eth.createAccount();
320 const collection = await helper.rft.mintCollection(alice);
321 const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
322
323 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
324 const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);
325
326 {
327 const result = await contract.methods.transfer(receiver, 50).send({from: owner});
328 const event = result.events.Transfer;
329 expect(event.address).to.be.equal(tokenAddress);
330 expect(event.returnValues.from).to.be.equal(owner);
331 expect(event.returnValues.to).to.be.equal(receiver);
332 expect(event.returnValues.value).to.be.equal('50');
333 }
334
335 {
336 const balance = await contract.methods.balanceOf(owner).call();
337 expect(+balance).to.equal(150);
338 }
339
340 {
341 const balance = await contract.methods.balanceOf(receiver).call();
342 expect(+balance).to.equal(50);
343 }
344 });
345
346 [337 [
347 'transfer',338 'transfer',
348 // 'transferCross', // TODO339 'transferCross',
349 ].map(testCase => 340 ].map(testCase =>
350 itEth(`Cannot ${testCase}() non-owned token`, async ({helper}) => {341 itEth(`Cannot ${testCase}() non-owned token`, async ({helper}) => {
342 const isCross = testCase === 'transferCross';
351 const owner = await helper.eth.createAccountWithBalance(donor);343 const owner = await helper.eth.createAccountWithBalance(donor);
352 const receiver = await helper.eth.createAccountWithBalance(donor);344 const ownerCross = helper.ethCrossAccount.fromAddress(owner);
345 const receiverEth = await helper.eth.createAccountWithBalance(donor);
346 const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
353 const collection = await helper.rft.mintCollection(alice);347 const collection = await helper.rft.mintCollection(alice);
354 const rftOwner = await collection.mintToken(alice, 10n, {Ethereum: owner});348 const rftOwner = await collection.mintToken(alice, 10n, {Ethereum: owner});
355 const rftReceiver = await collection.mintToken(alice, 10n, {Ethereum: receiver});349 const rftReceiver = await collection.mintToken(alice, 10n, {Ethereum: receiverEth});
356 const tokenIdNonExist = 9999999;350 const tokenIdNonExist = 9999999;
357 351
358 const tokenAddress1 = helper.ethAddress.fromTokenId(collection.collectionId, rftOwner.tokenId);352 const tokenAddress1 = helper.ethAddress.fromTokenId(collection.collectionId, rftOwner.tokenId);
363 const tokenEvmNonExist = helper.ethNativeContract.rftToken(tokenAddressNonExist, owner);357 const tokenEvmNonExist = helper.ethNativeContract.rftToken(tokenAddressNonExist, owner);
364 358
365 // 1. Can transfer zero amount (EIP-20):359 // 1. Can transfer zero amount (EIP-20):
366 await tokenEvmOwner.methods[testCase](receiver, 0).send({from: owner});360 await tokenEvmOwner.methods[testCase](isCross ? receiverCrossEth : receiverEth, 0).send({from: owner});
367 // 2. Cannot transfer non-owned token:361 // 2. Cannot transfer non-owned token:
368 await expect(tokenEvmReceiver.methods[testCase](owner, 0).send({from: owner})).to.be.rejected;362 await expect(tokenEvmReceiver.methods[testCase](isCross ? ownerCross : owner, 0).send({from: owner})).to.be.rejected;
369 await expect(tokenEvmReceiver.methods[testCase](owner, 5).send({from: owner})).to.be.rejected;363 await expect(tokenEvmReceiver.methods[testCase](isCross ? ownerCross : owner, 5).send({from: owner})).to.be.rejected;
370 // 3. Cannot transfer non-existing token:364 // 3. Cannot transfer non-existing token:
371 await expect(tokenEvmNonExist.methods[testCase](owner, 0).send({from: owner})).to.be.rejected;365 await expect(tokenEvmNonExist.methods[testCase](isCross ? ownerCross : owner, 0).send({from: owner})).to.be.rejected;
372 await expect(tokenEvmNonExist.methods[testCase](owner, 5).send({from: owner})).to.be.rejected;366 await expect(tokenEvmNonExist.methods[testCase](isCross ? ownerCross : owner, 5).send({from: owner})).to.be.rejected;
373367
374 // 4. Storage is not corrupted:368 // 4. Storage is not corrupted:
375 expect(await rftOwner.getTop10Owners()).to.deep.eq([{Ethereum: owner.toLowerCase()}]);369 expect(await rftOwner.getTop10Owners()).to.deep.eq([{Ethereum: owner.toLowerCase()}]);
376 expect(await rftReceiver.getTop10Owners()).to.deep.eq([{Ethereum: receiver.toLowerCase()}]);370 expect(await rftReceiver.getTop10Owners()).to.deep.eq([{Ethereum: receiverEth.toLowerCase()}]);
377 expect(await helper.rft.getTokenTop10Owners(collection.collectionId, tokenIdNonExist)).to.deep.eq([]); // TODO371 expect(await helper.rft.getTokenTop10Owners(collection.collectionId, tokenIdNonExist)).to.deep.eq([]);
378372
379 // 4.1 Tokens can be transferred:373 // 4.1 Tokens can be transferred:
380 await tokenEvmOwner.methods[testCase](receiver, 10).send({from: owner});374 await tokenEvmOwner.methods[testCase](isCross ? receiverCrossEth : receiverEth, 10).send({from: owner});
381 await tokenEvmReceiver.methods[testCase](owner, 10).send({from: receiver});375 await tokenEvmReceiver.methods[testCase](isCross ? ownerCross : owner, 10).send({from: receiverEth});
382 expect(await rftOwner.getTop10Owners()).to.deep.eq([{Ethereum: receiver.toLowerCase()}]);376 expect(await rftOwner.getTop10Owners()).to.deep.eq([{Ethereum: receiverEth.toLowerCase()}]);
383 expect(await rftReceiver.getTop10Owners()).to.deep.eq([{Ethereum: owner.toLowerCase()}]);377 expect(await rftReceiver.getTop10Owners()).to.deep.eq([{Ethereum: owner.toLowerCase()}]);
384 }));378 }));
385379
386 itEth('Can perform transferCross()', async ({helper}) => {
387 const owner = await helper.eth.createAccountWithBalance(donor);
388 const receiver = helper.eth.createAccount();
389 const receiverSub = (await helper.arrange.createAccounts([1n], donor))[0];
390 const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiver);
391 const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);
392 const collection = await helper.rft.mintCollection(alice);
393 const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
394
395 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
396 const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);
397
398 {
399 const result = await contract.methods.transferCross(receiverCrossEth, 50).send({from: owner});
400 const event = result.events.Transfer;
401 expect(event.address).to.be.equal(tokenAddress);
402 expect(event.returnValues.from).to.be.equal(owner);
403 expect(event.returnValues.to).to.be.equal(receiver);
404 expect(event.returnValues.value).to.be.equal('50');
405 }
406
407 {
408 const balance = await contract.methods.balanceOf(owner).call();
409 expect(+balance).to.equal(150);
410 }
411
412 {
413 const balance = await contract.methods.balanceOf(receiver).call();
414 expect(+balance).to.equal(50);
415 }
416
417 {
418 const result = await contract.methods.transferCross(receiverCrossSub, 50).send({from: owner});
419 const event = result.events.Transfer;
420 expect(event.address).to.be.equal(tokenAddress);
421 expect(event.returnValues.from).to.be.equal(owner);
422 expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(receiverSub.address));
423 expect(event.returnValues.value).to.be.equal('50');
424 }
425
426 {
427 const balance = await contract.methods.balanceOf(owner).call();
428 expect(+balance).to.equal(100);
429 }
430
431 {
432 const balance = await collection.getTokenBalance(tokenId, {Substrate: receiverSub.address});
433 expect(balance).to.equal(50n);
434 }
435 });
436 itEth('Can perform repartition()', async ({helper}) => {380 itEth('Can perform repartition()', async ({helper}) => {
437 const owner = await helper.eth.createAccountWithBalance(donor);381 const owner = await helper.eth.createAccountWithBalance(donor);
438 const receiver = await helper.eth.createAccountWithBalance(donor);382 const receiver = await helper.eth.createAccountWithBalance(donor);
deletedtests/src/rmrk/acceptNft.seqtest.tsdiffbeforeafterboth

no changes

deletedtests/src/rmrk/addResource.seqtest.tsdiffbeforeafterboth

no changes

deletedtests/src/rmrk/addTheme.seqtest.tsdiffbeforeafterboth

no changes

deletedtests/src/rmrk/burnNft.seqtest.tsdiffbeforeafterboth

no changes

deletedtests/src/rmrk/changeCollectionIssuer.seqtest.tsdiffbeforeafterboth

no changes

deletedtests/src/rmrk/createBase.seqtest.tsdiffbeforeafterboth

no changes

deletedtests/src/rmrk/createCollection.seqtest.tsdiffbeforeafterboth

no changes

deletedtests/src/rmrk/deleteCollection.seqtest.tsdiffbeforeafterboth

no changes

deletedtests/src/rmrk/equipNft.seqtest.tsdiffbeforeafterboth

no changes

deletedtests/src/rmrk/getOwnedNfts.seqtest.tsdiffbeforeafterboth

no changes

deletedtests/src/rmrk/lockCollection.seqtest.tsdiffbeforeafterboth

no changes

deletedtests/src/rmrk/mintNft.seqtest.tsdiffbeforeafterboth

no changes

deletedtests/src/rmrk/rejectNft.seqtest.tsdiffbeforeafterboth

no changes

deletedtests/src/rmrk/removeResource.seqtest.tsdiffbeforeafterboth

no changes

deletedtests/src/rmrk/rmrkIsolation.seqtest.tsdiffbeforeafterboth

no changes

deletedtests/src/rmrk/sendNft.seqtest.tsdiffbeforeafterboth

no changes

deletedtests/src/rmrk/setCollectionProperty.seqtest.tsdiffbeforeafterboth

no changes

deletedtests/src/rmrk/setEquippableList.seqtest.tsdiffbeforeafterboth

no changes

deletedtests/src/rmrk/setNftProperty.seqtest.tsdiffbeforeafterboth

no changes

deletedtests/src/rmrk/setResourcePriorities.seqtest.tsdiffbeforeafterboth

no changes

deletedtests/src/rmrk/util/fetch.tsdiffbeforeafterboth

no changes

deletedtests/src/rmrk/util/helpers.tsdiffbeforeafterboth

no changes

deletedtests/src/rmrk/util/tx.tsdiffbeforeafterboth

no changes

deletedtests/src/substrate/get-balance.tsdiffbeforeafterboth

no changes

deletedtests/src/substrate/privateKey.tsdiffbeforeafterboth

no changes

deletedtests/src/substrate/promisify-substrate.tsdiffbeforeafterboth

no changes

deletedtests/src/substrate/substrate-api.tsdiffbeforeafterboth

no changes

deletedtests/src/substrate/wait-new-blocks.tsdiffbeforeafterboth

no changes

modifiedtests/src/transfer.nload.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 {ApiPromise} from '@polkadot/api';17import os from 'os';
18import {IKeyringPair} from '@polkadot/types/types';18import {IKeyringPair} from '@polkadot/types/types';
19import usingApi, {submitTransactionAsync} from './substrate/substrate-api';19import {usingPlaygrounds} from './util';
20import waitNewBlocks from './substrate/wait-new-blocks';20import {UniqueHelper} from './util/playgrounds/unique';
21import * as cluster from 'cluster';21import * as notReallyCluster from 'cluster'; // https://github.com/nodejs/node/issues/42271#issuecomment-1063415346
22import os from 'os';22const cluster = notReallyCluster as unknown as notReallyCluster.Cluster;
2323
24async function findUnusedAddress(api: ApiPromise, privateKeyWrapper: (account: string) => IKeyringPair, seedAddition = ''): Promise<IKeyringPair> {24async function findUnusedAddress(helper: UniqueHelper, privateKey: (account: string) => Promise<IKeyringPair>, seedAddition = ''): Promise<IKeyringPair> {
25 let bal = 0n;25 let bal = 0n;
26 let unused;26 let unused;
27 do {27 do {
28 const randomSeed = 'seed' + Math.floor(Math.random() * Math.floor(10000)) + seedAddition;28 const randomSeed = 'seed' + Math.floor(Math.random() * Math.floor(10000)) + seedAddition;
29 unused = privateKeyWrapper(`//${randomSeed}`);29 unused = await privateKey(`//${randomSeed}`);
30 bal = (await api.query.system.account(unused.address)).data.free.toBigInt();30 bal = await helper.balance.getSubstrate(unused.address);
31 } while (bal !== 0n);31 } while (bal !== 0n);
32 return unused;32 return unused;
33}33}
3434
35function findUnusedAddresses(api: ApiPromise, privateKeyWrapper: (account: string) => IKeyringPair, amount: number): Promise<IKeyringPair[]> {35function findUnusedAddresses(helper: UniqueHelper, privateKey: (account: string) => Promise<IKeyringPair>, amount: number): Promise<IKeyringPair[]> {
36 return Promise.all(new Array(amount).fill(null).map(() => findUnusedAddress(api, privateKeyWrapper, '_' + Date.now())));36 return Promise.all(new Array(amount).fill(null).map(() => findUnusedAddress(helper, privateKey, '_' + Date.now())));
37}37}
3838
39// Innacurate transfer fee39// Innacurate transfer fee
54 counters = {};54 counters = {};
55}55}
5656
57async function distributeBalance(source: IKeyringPair, api: ApiPromise, totalAmount: bigint, stages: number) {57async function distributeBalance(source: IKeyringPair, helper: UniqueHelper, privateKey: (account: string) => Promise<IKeyringPair>, totalAmount: bigint, stages: number) {
58 const accounts = [source];58 const accounts = [source];
59 // we don't need source in output array59 // we don't need source in output array
60 const failedAccounts = [0];60 const failedAccounts = [0];
6161
62 const finalUserAmount = 2 ** stages - 1;62 const finalUserAmount = 2 ** stages - 1;
63 accounts.push(...await findUnusedAddresses(api, finalUserAmount));63 accounts.push(...await findUnusedAddresses(helper, privateKey, finalUserAmount));
64 // findUnusedAddresses produces at least 1 request per user64 // findUnusedAddresses produces at least 1 request per user
65 increaseCounter('requests', finalUserAmount);65 increaseCounter('requests', finalUserAmount);
6666
67 for (let stage = 0; stage < stages; stage++) {67 for (let stage = 0; stage < stages; stage++) {
68 const usersWithBalance = 2 ** stage;68 const usersWithBalance = 2 ** stage;
69 const amount = totalAmount / (2n ** BigInt(stage)) - FEE * BigInt(stage);69 const amount = totalAmount / (2n ** BigInt(stage)) - FEE * BigInt(stage);
70 // console.log(`Stage ${stage}/${stages}, ${usersWithBalance} => ${usersWithBalance * 2} = ${amount}`);70 // console.log(`Stage ${stage}/${stages}, ${usersWithBalance} => ${usersWithBalance * 2} = ${amount}`);
71 const txs = [];71 const txs: Promise<boolean | void>[] = [];
72 for (let i = 0; i < usersWithBalance; i++) {72 for (let i = 0; i < usersWithBalance; i++) {
73 const newUser = accounts[i + usersWithBalance];73 const newUser = accounts[i + usersWithBalance];
74 // console.log(`${accounts[i].address} => ${newUser.address} = ${amountToSplit}`);74 // console.log(`${accounts[i].address} => ${newUser.address} = ${amountToSplit}`);
75 const tx = api.tx.balances.transfer(newUser.address, amount);75 const tx = helper.balance.transferToSubstrate(accounts[i], newUser.address, amount);
76 txs.push(submitTransactionAsync(accounts[i], tx).catch(() => {76 txs.push(tx.catch(() => {
77 failedAccounts.push(i + usersWithBalance);77 failedAccounts.push(i + usersWithBalance);
78 increaseCounter('txFailed', 1);78 increaseCounter('txFailed', 1);
79 }));79 }));
9090
91if (cluster.isMaster) {91if (cluster.isMaster) {
92 let testDone = false;92 let testDone = false;
93 usingApi(async (api) => {93 usingPlaygrounds(async (helper) => {
94 const prevCounters: { [key: string]: number } = {};94 const prevCounters: { [key: string]: number } = {};
95 while (!testDone) {95 while (!testDone) {
96 for (const name in counters) {96 for (const name in counters) {
103 console.log(`${name.padEnd(15)} = ${counters[name] - prevCounters[name]}`);103 console.log(`${name.padEnd(15)} = ${counters[name] - prevCounters[name]}`);
104 prevCounters[name] = counters[name];104 prevCounters[name] = counters[name];
105 }105 }
106 await waitNewBlocks(api, 1);106 await helper.wait.newBlocks(1);
107 }107 }
108 });108 });
109 const waiting: Promise<void>[] = [];109 const waiting: Promise<void>[] = [];
110 console.log(`Starting ${os.cpus().length} workers`);110 console.log(`Starting ${os.cpus().length} workers`);
111 usingApi(async (api, privateKeyWrapper) => {111 usingPlaygrounds(async (helper, privateKey) => {
112 const alice = privateKeyWrapper('//Alice');112 const alice = await privateKey('//Alice');
113 for (const id in os.cpus()) {113 for (const id in os.cpus()) {
114 const WORKER_NAME = `//LoadWorker${id}_${Date.now()}`;114 const WORKER_NAME = `//LoadWorker${id}_${Date.now()}`;
115 const workerAccount = privateKeyWrapper(WORKER_NAME);115 const workerAccount = await privateKey(WORKER_NAME);
116 const tx = api.tx.balances.transfer(workerAccount.address, 400n * 10n ** 23n);116 await helper.balance.transferToSubstrate(alice, workerAccount.address, 400n * 10n ** 23n);
117 await submitTransactionAsync(alice, tx);
118117
119 const worker = cluster.fork({118 const worker = cluster.fork({
120 WORKER_NAME,119 WORKER_NAME,
132 });131 });
133} else {132} else {
134 increaseCounter('startedWorkers', 1);133 increaseCounter('startedWorkers', 1);
135 usingApi(async (api, privateKeyWrapper) => {134 usingPlaygrounds(async (helper, privateKey) => {
136 await distributeBalance(privateKeyWrapper(process.env.WORKER_NAME as string), api, 400n * 10n ** 22n, 10);135 await distributeBalance(await privateKey(process.env.WORKER_NAME as string), helper, privateKey, 400n * 10n ** 22n, 10);
137 });136 });
138 const interval = setInterval(() => {137 const interval = setInterval(() => {
139 flushCounterToMaster();138 flushCounterToMaster();