git.delta.rocks / unique-network / refs/commits / 7f0477cd03b0

difftreelog

Merge branch 'feature/app-staking' of https://github.com/UniqueNetwork/unique-chain into feature/app-staking

PraetorP2022-08-31parents: #dffea63 #36c9f56.patch.diff
in: master

4 files changed

modifiedtests/src/addCollectionAdmin.test.tsdiffbeforeafterboth
33describe('Integration Test addCollectionAdmin(collection_id, new_admin_id):', () => {33describe('Integration Test addCollectionAdmin(collection_id, new_admin_id):', () => {
34 it('Add collection admin.', async () => {34 it('Add collection admin.', async () => {
35 await usingPlaygrounds(async (helper) => {35 await usingPlaygrounds(async (helper) => {
36 const [alice, bob] = await helper.arrange.creteAccounts([10n, 10n], donor);36 const [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor);
37 const {collectionId} = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});37 const {collectionId} = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});
3838
39 const collection = await helper.collection.getData(collectionId);39 const collection = await helper.collection.getData(collectionId);
50describe('Negative Integration Test addCollectionAdmin(collection_id, new_admin_id):', () => {50describe('Negative Integration Test addCollectionAdmin(collection_id, new_admin_id):', () => {
51 it("Not owner can't add collection admin.", async () => {51 it("Not owner can't add collection admin.", async () => {
52 await usingPlaygrounds(async (helper) => {52 await usingPlaygrounds(async (helper) => {
53 const [alice, bob, charlie] = await helper.arrange.creteAccounts([10n, 10n, 10n], donor);53 const [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
54 const {collectionId} = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});54 const {collectionId} = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});
5555
56 const collection = await helper.collection.getData(collectionId);56 const collection = await helper.collection.getData(collectionId);
6969
70 it("Admin can't add collection admin.", async () => {70 it("Admin can't add collection admin.", async () => {
71 await usingPlaygrounds(async (helper) => {71 await usingPlaygrounds(async (helper) => {
72 const [alice, bob, charlie] = await helper.arrange.creteAccounts([10n, 10n, 10n], donor);72 const [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
73 const collection = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});73 const collection = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});
7474
75 await collection.addAdmin(alice, {Substrate: bob.address});75 await collection.addAdmin(alice, {Substrate: bob.address});
8888
89 it("Can't add collection admin of not existing collection.", async () => {89 it("Can't add collection admin of not existing collection.", async () => {
90 await usingPlaygrounds(async (helper) => {90 await usingPlaygrounds(async (helper) => {
91 const [alice, bob] = await helper.arrange.creteAccounts([10n, 10n, 10n], donor);91 const [alice, bob] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
92 // tslint:disable-next-line: no-bitwise92 // tslint:disable-next-line: no-bitwise
93 const collectionId = (1 << 32) - 1;93 const collectionId = (1 << 32) - 1;
9494
102102
103 it("Can't add an admin to a destroyed collection.", async () => {103 it("Can't add an admin to a destroyed collection.", async () => {
104 await usingPlaygrounds(async (helper) => {104 await usingPlaygrounds(async (helper) => {
105 const [alice, bob] = await helper.arrange.creteAccounts([10n, 10n, 10n], donor);105 const [alice, bob] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
106 const collection = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});106 const collection = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});
107107
108 await collection.burn(alice);108 await collection.burn(alice);
116116
117 it('Add an admin to a collection that has reached the maximum number of admins limit', async () => {117 it('Add an admin to a collection that has reached the maximum number of admins limit', async () => {
118 await usingPlaygrounds(async (helper) => {118 await usingPlaygrounds(async (helper) => {
119 const [alice, ...accounts] = await helper.arrange.creteAccounts([10n, 0n, 0n, 0n, 0n, 0n, 0n, 0n], donor);119 const [alice, ...accounts] = await helper.arrange.createAccounts([10n, 0n, 0n, 0n, 0n, 0n, 0n, 0n], donor);
120 const collection = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});120 const collection = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});
121121
122 const chainAdminLimit = (helper.api!.consts.common.collectionAdminsLimit as any).toNumber();122 const chainAdminLimit = (helper.api!.consts.common.collectionAdminsLimit as any).toNumber();
modifiedtests/src/app-promotion.test.tsdiffbeforeafterboth
28import {encodeAddress} from '@polkadot/util-crypto';28import {encodeAddress} from '@polkadot/util-crypto';
29import {stringToU8a} from '@polkadot/util';29import {stringToU8a} from '@polkadot/util';
30import {ApiPromise} from '@polkadot/api';30import {ApiPromise} from '@polkadot/api';
31import {contractHelpers, createEthAccountWithBalance, deployFlipper, itWeb3} from './eth/util/helpers';31import {SponsoringMode, contractHelpers, createEthAccountWithBalance, deployFlipper, itWeb3, transferBalanceToEth} from './eth/util/helpers';
32chai.use(chaiAsPromised);32chai.use(chaiAsPromised);
33const expect = chai.expect;33const expect = chai.expect;
3434
66 it('should change balance state to "locked", add it to "staked" map, and increase "totalStaked" amount', async () => {66 it('should change balance state to "locked", add it to "staked" map, and increase "totalStaked" amount', async () => {
67 await usingPlaygrounds(async (helper) => {67 await usingPlaygrounds(async (helper) => {
68 const totalStakedBefore = await helper.staking.getTotalStaked();68 const totalStakedBefore = await helper.staking.getTotalStaked();
69 const [staker] = await helper.arrange.creteAccounts([400n], alice);69 const [staker] = await helper.arrange.createAccounts([400n], alice);
70 70
71 // Minimum stake amount is 100:71 // Minimum stake amount is 100:
72 await expect(helper.staking.stake(staker, 100n * nominal - 1n)).to.be.eventually.rejected;72 await expect(helper.staking.stake(staker, 100n * nominal - 1n)).to.be.eventually.rejected;
89 89
90 it('should reject transaction if stake amount is more than total free balance', async () => {90 it('should reject transaction if stake amount is more than total free balance', async () => {
91 await usingPlaygrounds(async helper => { 91 await usingPlaygrounds(async helper => {
92 const [staker] = await helper.arrange.creteAccounts([300n], alice);92 const [staker] = await helper.arrange.createAccounts([300n], alice);
9393
94 // Can't stake full balance because Alice needs to pay some fee94 // Can't stake full balance because Alice needs to pay some fee
95 await expect(helper.staking.stake(staker, 300n * nominal)).to.be.eventually.rejected;95 await expect(helper.staking.stake(staker, 300n * nominal)).to.be.eventually.rejected;
103 103
104 it('for different accounts in one block is possible', async () => {104 it('for different accounts in one block is possible', async () => {
105 await usingPlaygrounds(async helper => {105 await usingPlaygrounds(async helper => {
106 const crowd = await helper.arrange.creteAccounts([1000n, 1000n, 1000n, 1000n], alice);106 const crowd = await helper.arrange.createAccounts([1000n, 1000n, 1000n, 1000n], alice);
107 107
108 const crowdStartsToStake = crowd.map(user => helper.staking.stake(user, 100n * nominal));108 const crowdStartsToStake = crowd.map(user => helper.staking.stake(user, 100n * nominal));
109 await expect(Promise.all(crowdStartsToStake)).to.be.eventually.fulfilled;109 await expect(Promise.all(crowdStartsToStake)).to.be.eventually.fulfilled;
121 it('should change balance state to "reserved", add it to "pendingUnstake" map, and subtract it from totalStaked', async () => {121 it('should change balance state to "reserved", add it to "pendingUnstake" map, and subtract it from totalStaked', async () => {
122 await usingPlaygrounds(async helper => {122 await usingPlaygrounds(async helper => {
123 const totalStakedBefore = await helper.staking.getTotalStaked();123 const totalStakedBefore = await helper.staking.getTotalStaked();
124 const [staker] = await helper.arrange.creteAccounts([1000n], alice);124 const [staker] = await helper.arrange.createAccounts([1000n], alice);
125 await helper.staking.stake(staker, 500n * nominal);125 await helper.staking.stake(staker, 500n * nominal);
126 await helper.staking.unstake(staker);126 await helper.staking.unstake(staker);
127127
133133
134 it('should remove multiple stakes', async () => {134 it('should remove multiple stakes', async () => {
135 await usingPlaygrounds(async helper => {135 await usingPlaygrounds(async helper => {
136 const [staker] = await helper.arrange.creteAccounts([1000n], alice);136 const [staker] = await helper.arrange.createAccounts([1000n], alice);
137 await helper.staking.stake(staker, 100n * nominal);137 await helper.staking.stake(staker, 100n * nominal);
138 await helper.staking.stake(staker, 200n * nominal);138 await helper.staking.stake(staker, 200n * nominal);
139 await helper.staking.stake(staker, 300n * nominal);139 await helper.staking.stake(staker, 300n * nominal);
159159
160 it('should not have any effects if no active stakes', async () => {160 it('should not have any effects if no active stakes', async () => {
161 await usingPlaygrounds(async (helper) => {161 await usingPlaygrounds(async (helper) => {
162 const [staker] = await helper.arrange.creteAccounts([1000n], alice);162 const [staker] = await helper.arrange.createAccounts([1000n], alice);
163 163
164 // unstake has no effect if no stakes at all164 // unstake has no effect if no stakes at all
165 await helper.staking.unstake(staker);165 await helper.staking.unstake(staker);
182182
183 it('should keep different unlocking block for each unlocking stake', async () => {183 it('should keep different unlocking block for each unlocking stake', async () => {
184 await usingPlaygrounds(async (helper) => {184 await usingPlaygrounds(async (helper) => {
185 const [staker] = await helper.arrange.creteAccounts([1000n], alice);185 const [staker] = await helper.arrange.createAccounts([1000n], alice);
186 await helper.staking.stake(staker, 100n * nominal);186 await helper.staking.stake(staker, 100n * nominal);
187 await helper.staking.unstake(staker);187 await helper.staking.unstake(staker);
188 await helper.staking.stake(staker, 100n * nominal);188 await helper.staking.stake(staker, 120n * nominal);
189 await helper.staking.unstake(staker);189 await helper.staking.unstake(staker);
190
190 expect.fail('Not implemented');191 const unstakingPerBlock = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});
192 expect(unstakingPerBlock).has.length(2);
193 expect(unstakingPerBlock[0][1]).to.equal(100n * nominal);
194 expect(unstakingPerBlock[1][1]).to.equal(120n * nominal);
191 });195 });
192 });196 });
193197
194 it('should unlock balance after unlocking period ends and subtract it from "pendingUnstake"', async () => {198 it('should unlock balance after unlocking period ends and subtract it from "pendingUnstake"', async () => {
195 await usingPlaygrounds(async (helper) => {199 await usingPlaygrounds(async (helper) => {
196 const [staker] = await helper.arrange.creteAccounts([1000n], alice);200 const [staker] = await helper.arrange.createAccounts([1000n], alice);
197 await helper.staking.stake(staker, 100n * nominal);201 await helper.staking.stake(staker, 100n * nominal);
198 await helper.staking.unstake(staker);202 await helper.staking.unstake(staker);
199 // get unstake from block
200 // wait it
201 // check balance returned
202 expect.fail('Not implemented');203 expect.fail('Not implemented');
203 });204 });
204 });205 });
205206
206 it('should be possible for different accounts in one block', async () => {207 it('should be possible for different accounts in one block', async () => {
207 await usingPlaygrounds(async (helper) => {208 await usingPlaygrounds(async (helper) => {
208 const stakers = await helper.arrange.creteAccounts([200n, 200n, 200n, 200n, 200n], alice);209 const stakers = await helper.arrange.createAccounts([200n, 200n, 200n, 200n, 200n], alice);
209210
210 await Promise.all(stakers.map(staker => helper.staking.stake(staker, 100n * nominal)));211 await Promise.all(stakers.map(staker => helper.staking.stake(staker, 100n * nominal)));
211 await Promise.all(stakers.map(staker => helper.staking.unstake(staker)));212 await Promise.all(stakers.map(staker => helper.staking.unstake(staker)));
236 // We are not going to set an eth address as a sponsor,237 // We are not going to set an eth address as a sponsor,
237 // but we do want to check, it doesn't break anything;238 // but we do want to check, it doesn't break anything;
238 await usingPlaygrounds(async (helper) => {239 await usingPlaygrounds(async (helper) => {
239 const [charlie] = await helper.arrange.creteAccounts([10n], alice);240 const [charlie] = await helper.arrange.createAccounts([10n], alice);
240 const ethCharlie = helper.address.substrateToEth(charlie.address); 241 const ethCharlie = helper.address.substrateToEth(charlie.address);
241 // Alice sets Ethereum address as a sudo. Then Substrate address back...242 // Alice sets Ethereum address as a sudo. Then Substrate address back...
242 await expect(helper.signTransaction(alice, helper.api!.tx.sudo.sudo(helper.api!.tx.promotion.setAdminAddress({Ethereum: ethCharlie})))).to.be.eventually.fulfilled;243 await expect(helper.signTransaction(alice, helper.api!.tx.sudo.sudo(helper.api!.tx.promotion.setAdminAddress({Ethereum: ethCharlie})))).to.be.eventually.fulfilled;
250251
251 it('can be reassigned', async () => {252 it('can be reassigned', async () => {
252 await usingPlaygrounds(async (helper) => {253 await usingPlaygrounds(async (helper) => {
253 const [oldAdmin, newAdmin, collectionOwner] = await helper.arrange.creteAccounts([10n, 10n, 10n], alice);254 const [oldAdmin, newAdmin, collectionOwner] = await helper.arrange.createAccounts([10n, 10n, 10n], alice);
254 const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});255 const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});
255 256
256 await expect(helper.signTransaction(alice, helper.api!.tx.sudo.sudo(helper.api!.tx.promotion.setAdminAddress(normalizeAccountId(oldAdmin))))).to.be.eventually.fulfilled;257 await expect(helper.signTransaction(alice, helper.api!.tx.sudo.sudo(helper.api!.tx.promotion.setAdminAddress(normalizeAccountId(oldAdmin))))).to.be.eventually.fulfilled;
269 await helper.signTransaction(alice, tx);270 await helper.signTransaction(alice, tx);
270 });271 });
271 });272 });
273
272 274 it('should actually sponsor transactions', async () => {
275 await usingPlaygrounds(async (helper) => {
276 const [collectionOwner, tokenSender, receiver] = await helper.arrange.createAccounts([10n, 10n, 0n], alice);
277 const collection = await helper.nft.mintCollection(collectionOwner, {name: 'Name', description: 'Description', tokenPrefix: 'Prefix', limits: {sponsorTransferTimeout: 0}});
278 const token = await collection.mintToken(collectionOwner, {Substrate: tokenSender.address});
279 await helper.signTransaction(palletAdmin, helper.api!.tx.promotion.sponsorCollection(collection.collectionId));
280 const palletBalanceBefore = await helper.balance.getSubstrate(palletAddress);
281
282 await token.transfer(tokenSender, {Substrate: receiver.address});
283 expect (await token.getOwner()).to.be.deep.equal({Substrate: receiver.address});
284 const palletBalanceAfter = await helper.balance.getSubstrate(palletAddress);
285
286 // senders balance the same
287 expect (await helper.balance.getSubstrate(tokenSender.address)).to.be.equal(10n * nominal);
288 expect (palletBalanceBefore > palletBalanceAfter).to.be.true;
289 });
290 });
291
273 it('can not be set by non admin', async () => {292 it('can not be set by non admin', async () => {
274 await usingPlaygrounds(async (helper) => {293 await usingPlaygrounds(async (helper) => {
275 const [collectionOwner, nonAdmin] = await helper.arrange.creteAccounts([10n, 10n], alice);294 const [collectionOwner, nonAdmin] = await helper.arrange.createAccounts([10n, 10n], alice);
276295
277 const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});296 const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});
278 297
283302
284 it('should set pallet address as confirmed admin', async () => {303 it('should set pallet address as confirmed admin', async () => {
285 await usingPlaygrounds(async (helper) => {304 await usingPlaygrounds(async (helper) => {
286 const [collectionOwner, oldSponsor] = await helper.arrange.creteAccounts([20n, 20n], alice);305 const [collectionOwner, oldSponsor] = await helper.arrange.createAccounts([20n, 20n], alice);
287 306
288 // Can set sponsoring for collection without sponsor307 // Can set sponsoring for collection without sponsor
289 const collectionWithoutSponsor = await helper.nft.mintCollection(collectionOwner, {name: 'No-sponsor', description: 'New Collection', tokenPrefix: 'Promotion'});308 const collectionWithoutSponsor = await helper.nft.mintCollection(collectionOwner, {name: 'No-sponsor', description: 'New Collection', tokenPrefix: 'Promotion'});
306325
307 it('can be overwritten by collection owner', async () => { 326 it('can be overwritten by collection owner', async () => {
308 await usingPlaygrounds(async (helper) => {327 await usingPlaygrounds(async (helper) => {
309 const [collectionOwner, newSponsor] = await helper.arrange.creteAccounts([20n, 0n], alice);328 const [collectionOwner, newSponsor] = await helper.arrange.createAccounts([20n, 0n], alice);
310 const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});329 const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});
311 const collectionId = collection.collectionId;330 const collectionId = collection.collectionId;
312 331
341360
342 it('should reject transaction if collection was burnt', async () => {361 it('should reject transaction if collection was burnt', async () => {
343 await usingPlaygrounds(async (helper) => {362 await usingPlaygrounds(async (helper) => {
344 const [collectionOwner] = await helper.arrange.creteAccounts([10n], alice);363 const [collectionOwner] = await helper.arrange.createAccounts([10n], alice);
345 const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});364 const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});
346 await collection.burn(collectionOwner);365 await collection.burn(collectionOwner);
347366
353describe('app-promotion stopSponsoringCollection', () => {372describe('app-promotion stopSponsoringCollection', () => {
354 it('can not be called by non-admin', async () => { 373 it('can not be called by non-admin', async () => {
355 await usingPlaygrounds(async (helper) => {374 await usingPlaygrounds(async (helper) => {
356 const [collectionOwner, nonAdmin] = await helper.arrange.creteAccounts([10n, 10n], alice);375 const [collectionOwner, nonAdmin] = await helper.arrange.createAccounts([10n, 10n], alice);
357 const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});376 const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});
358 377
359 await expect(helper.signTransaction(palletAdmin, helper.api!.tx.promotion.sponsorCollection(collection.collectionId))).to.be.eventually.fulfilled;378 await expect(helper.signTransaction(palletAdmin, helper.api!.tx.promotion.sponsorCollection(collection.collectionId))).to.be.eventually.fulfilled;
365384
366 it('should set sponsoring as disabled', async () => {385 it('should set sponsoring as disabled', async () => {
367 await usingPlaygrounds(async (helper) => {386 await usingPlaygrounds(async (helper) => {
368 const [collectionOwner] = await helper.arrange.creteAccounts([10n, 10n], alice);387 const [collectionOwner] = await helper.arrange.createAccounts([10n, 10n], alice);
369 const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});388 const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});
370 389
371 await expect(helper.signTransaction(palletAdmin, helper.api!.tx.promotion.sponsorCollection(collection.collectionId))).to.be.eventually.fulfilled;390 await expect(helper.signTransaction(palletAdmin, helper.api!.tx.promotion.sponsorCollection(collection.collectionId))).to.be.eventually.fulfilled;
377396
378 it('should not affect collection which is not sponsored by pallete', async () => {397 it('should not affect collection which is not sponsored by pallete', async () => {
379 await usingPlaygrounds(async (helper) => {398 await usingPlaygrounds(async (helper) => {
380 const [collectionOwner] = await helper.arrange.creteAccounts([10n, 10n], alice);399 const [collectionOwner] = await helper.arrange.createAccounts([10n, 10n], alice);
381 const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion', pendingSponsor: collectionOwner.address});400 const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion', pendingSponsor: collectionOwner.address});
382 await collection.confirmSponsorship(collectionOwner);401 await collection.confirmSponsorship(collectionOwner);
383 402
389408
390 it('should reject transaction if collection does not exist', async () => { 409 it('should reject transaction if collection does not exist', async () => {
391 await usingPlaygrounds(async (helper) => {410 await usingPlaygrounds(async (helper) => {
392 const [collectionOwner] = await helper.arrange.creteAccounts([10n], alice);411 const [collectionOwner] = await helper.arrange.createAccounts([10n], alice);
393 const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});412 const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});
394 413
395 await collection.burn(collectionOwner);414 await collection.burn(collectionOwner);
471490
472 itWeb3('can not be set by non admin', async ({api, web3, privateKeyWrapper}) => {491 itWeb3('can not be set by non admin', async ({api, web3, privateKeyWrapper}) => {
473 await usingPlaygrounds(async (helper) => { 492 await usingPlaygrounds(async (helper) => {
474 const [nonAdmin] = await helper.arrange.creteAccounts([50n], alice);493 const [nonAdmin] = await helper.arrange.createAccounts([50n], alice);
475 const contractOwner = (await createEthAccountWithBalance(api, web3, privateKeyWrapper)).toLowerCase();494 const contractOwner = (await createEthAccountWithBalance(api, web3, privateKeyWrapper)).toLowerCase();
476 const flipper = await deployFlipper(web3, contractOwner);495 const flipper = await deployFlipper(web3, contractOwner);
477 const contractMethods = contractHelpers(web3, contractOwner);496 const contractMethods = contractHelpers(web3, contractOwner);
488 },507 },
489 });508 });
490 });509 });
491 });
492510
493 it('will return unused gas fee to app-promotion pallete', async () => {511 itWeb3('should be rejected for non-contract address', async ({api, web3, privateKeyWrapper}) => {
494 // TODO512 await usingPlaygrounds(async (helper) => {
495 // arrange: Alice deploys Flipper
496 // arrange: Admin calls appPromotion.sponsorContract(Flipper.address)
497513
498 // assert: Bob calls Flipper - expect balances deposit event do not appears for Bob /// Unused gas fee returns to contract514 });
499 // assert: Bobs balance the same515 });
500 });516 });
501517
502 it('will failed for non contract address', async () => {518 itWeb3('should actually sponsor transactions', async ({api, web3, privateKeyWrapper}) => {
519 await usingPlaygrounds(async (helper) => {
503 // arrange: web3 creates new address - 0x0520 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
521 const contractOwner = (await createEthAccountWithBalance(api, web3, privateKeyWrapper)).toLowerCase();
522 const flipper = await deployFlipper(web3, contractOwner);
523 const contractHelper = contractHelpers(web3, contractOwner);
524 await contractHelper.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: contractOwner});
525 await contractHelper.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: contractOwner});
526 await transferBalanceToEth(api, alice, flipper.options.address, 1000n);
504527
505 // assert: Admin calls appPromotion.sponsorContract(0x0) throws528 await helper.signTransaction(palletAdmin, api.tx.promotion.sponsorConract(flipper.options.address));
506 // assert: Admin calls appPromotion.sponsorContract(Substrate address) throws529 await flipper.methods.flip().send({from: caller});
507 });530 expect(await flipper.methods.getValue().call()).to.be.true;
508531
509 it('will actually sponsor transactions', async () => {532 const callerBalance = await helper.balance.getEthereum(caller);
510 // TODO test it because this is a new way of contract sponsoring533 const contractBalanceAfter = await helper.balance.getEthereum(flipper.options.address);
511 });
512});
513534
514describe('app-promotion stopSponsoringContract', () => {535 expect(callerBalance).to.be.equal(1000n * nominal);
515 before(async function () {
516 await usingPlaygrounds(async (helper, privateKeyWrapper) => {
517 if (!getModuleNames(helper.api!).includes(Pallets.AppPromotion)) this.skip();
518 alice = privateKeyWrapper('//Alice');
519 bob = privateKeyWrapper('//Bob');
520 palletAdmin = privateKeyWrapper('//palletAdmin');
521 await helper.balance.transferToSubstrate(alice, palletAdmin.address, 10n * helper.balance.getOneTokenNominal());
522 await helper.balance.transferToSubstrate(alice, palletAddress, 10n * helper.balance.getOneTokenNominal());536 expect(1000n * nominal > contractBalanceAfter).to.be.true;
523
524 const tx = helper.api!.tx.sudo.sudo(helper.api!.tx.promotion.setAdminAddress(normalizeAccountId(palletAdmin)));
525 await helper.signTransaction(alice, tx);
526
527 nominal = helper.balance.getOneTokenNominal();
528 });537 });
529 });538 });
530
531 it('will set contract sponsoring mode as disabled', async () => {539});
532 // arrange: Alice deploys Flipper
533 // arrange: Admin calls appPromotion.sponsorContract(Flipper.address)
534
535 // act: Admin calls appPromotion.stopSponsoringContract(Flipper.address)
536 // assert: contract sponsoring mode = TODO
537540
538 // act: Bob calls Flipper541describe('app-promotion stopSponsoringContract', () => {
542 itWeb3('should remove pallet address from contract sponsors', async ({api, web3, privateKeyWrapper}) => {
543 await usingPlaygrounds(async (helper) => {
544 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
545 const contractOwner = (await createEthAccountWithBalance(api, web3, privateKeyWrapper)).toLowerCase();
546 const flipper = await deployFlipper(web3, contractOwner);
547 await transferBalanceToEth(api, alice, flipper.options.address);
548 const contractHelper = contractHelpers(web3, contractOwner);
549 await contractHelper.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: contractOwner});
550 await helper.signTransaction(palletAdmin, api.tx.promotion.sponsorConract(flipper.options.address));
551 await helper.signTransaction(palletAdmin, api.tx.promotion.stopSponsoringContract(flipper.options.address));
539552
540 // assert: PalleteAddress balance did not change553 expect(await contractHelper.methods.hasSponsor(flipper.options.address).call()).to.be.false;
554 expect((await api.query.evmContractHelpers.owner(flipper.options.address)).toJSON()).to.be.equal(contractOwner);
541 // assert: Bobs balance less than before /// Bob payed some fee555 expect((await api.query.evmContractHelpers.sponsoring(flipper.options.address)).toJSON()).to.deep.equal({
556 disabled: null,
542 });557 });
543558
544 it('can not be called by non-admin', async () => {559 await flipper.methods.flip().send({from: caller});
545 // arrange: Alice deploys Flipper
546 // arrange: Admin calls appPromotion.sponsorContract(Flipper.address)560 expect(await flipper.methods.getValue().call()).to.be.true;
547561
548 // assert: Random calls appPromotion.stopSponsoringContract(Flipper.address) throws562 const callerBalance = await helper.balance.getEthereum(caller);
563 const contractBalanceAfter = await helper.balance.getEthereum(flipper.options.address);
564
549 // assert: contract sponsor is PallereAddress565 // caller payed for call
566 expect(1000n * nominal > callerBalance).to.be.true;
567 expect(contractBalanceAfter).to.be.equal(1000n * nominal);
568 });
550 });569 });
551570
552 it('will not affect a contract which is not sponsored by pallete', async () => {571 itWeb3('can not be called by non-admin', async ({api, web3, privateKeyWrapper}) => {
553 // arrange: Alice deploys Flipper572 await usingPlaygrounds(async (helper) => {
554 // arrange: Alice sets self sponsoring for Flipper573 const [nonAdmin] = await helper.arrange.createAccounts([10n], alice);
555
556 // act: Admin calls appPromotion.stopSponsoringContract(Flipper.address) throws574 const contractOwner = (await createEthAccountWithBalance(api, web3, privateKeyWrapper)).toLowerCase();
575 const flipper = await deployFlipper(web3, contractOwner);
557576
558 // assert: contract.sponsoringMode = Self577 await helper.signTransaction(palletAdmin, api.tx.promotion.sponsorConract(flipper.options.address));
559 // assert: contract.sponsor to be contract578 await expect(helper.signTransaction(nonAdmin, api.tx.promotion.stopSponsoringContract(flipper.options.address))).to.be.rejected;
579 });
560 });580 });
561581
562 it('will failed for non contract address', async () => {582 itWeb3('should not affect a contract which is not sponsored by pallete', async ({api, web3, privateKeyWrapper}) => {
563 // arrange: web3 creates new address - 0x0583 await usingPlaygrounds(async (helper) => {
584 const [nonAdmin] = await helper.arrange.createAccounts([10n], alice);
585 const contractOwner = (await createEthAccountWithBalance(api, web3, privateKeyWrapper)).toLowerCase();
586 const flipper = await deployFlipper(web3, contractOwner);
587 const contractHelper = contractHelpers(web3, contractOwner);
588 await expect(contractHelper.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.not.rejected;
564589
565 // expect stopSponsoringContract(0x0) throws590 await expect(helper.signTransaction(nonAdmin, api.tx.promotion.stopSponsoringContract(flipper.options.address))).to.be.rejected;
591 });
566 });592 });
567});593});
568594
569describe('app-promotion rewards', () => {595describe('app-promotion rewards', () => {
570 it('should credit 0.05% for staking period', async () => { 596 it('should credit 0.05% for staking period', async () => {
571 await usingPlaygrounds(async helper => {597 await usingPlaygrounds(async helper => {
572 const [staker] = await helper.arrange.creteAccounts([5000n], alice);598 const [staker] = await helper.arrange.createAccounts([5000n], alice);
573 599
574 await helper.staking.stake(staker, 100n * nominal);600 await helper.staking.stake(staker, 100n * nominal);
575 await helper.staking.stake(staker, 200n * nominal);601 await helper.staking.stake(staker, 200n * nominal);
580 expect(totalStakedPerBlock).to.be.deep.equal([calculateIncome(100n * nominal, 10n), calculateIncome(200n * nominal, 10n)]);606 expect(totalStakedPerBlock).to.be.deep.equal([calculateIncome(100n * nominal, 10n), calculateIncome(200n * nominal, 10n)]);
581 });607 });
582 });608 });
609
610 it('can not be initialized by non admin', async () => {
611 await usingPlaygrounds(async (helper) => {
612 expect.fail('Test not implemented');
613 });
614 });
615
616 it('shoud be paid for more than one period if payments was missed', async () => {
617 await usingPlaygrounds(async (helper) => {
618 expect.fail('Test not implemented');
619 });
620 });
583 621
584 it('should not be credited for unstaked (reserved) balance', async () => {622 it('should not be credited for unstaked (reserved) balance', async () => {
585 await usingPlaygrounds(async helper => {623 await usingPlaygrounds(async helper => {
589 627
590 it('should bring compound interest', async () => {628 it('should bring compound interest', async () => {
591 await usingPlaygrounds(async helper => {629 await usingPlaygrounds(async helper => {
592 const [staker] = await helper.arrange.creteAccounts([800n], alice);630 const [staker] = await helper.arrange.createAccounts([800n], alice);
593 631
594 await helper.staking.stake(staker, 100n * nominal);632 await helper.staking.stake(staker, 100n * nominal);
595 await helper.staking.stake(staker, 200n * nominal);633 await helper.staking.stake(staker, 200n * nominal);
607 });645 });
608 });646 });
609647
610 // TODO (load test. Can pay reward for 10000 addresses)648 it.skip('can handle 40.000 rewards', async () => {
649 await usingPlaygrounds(async (helper) => {
650 const [donor] = await helper.arrange.createAccounts([7_000_000n], alice);
651 const crowdStakes = async () => {
652 // each account in the crowd stakes 2 times
653 const crowd = await helper.arrange.createCrowd(500, 300n, donor);
654 await Promise.all(crowd.map(account => helper.staking.stake(account, 100n * nominal)));
655 await Promise.all(crowd.map(account => helper.staking.stake(account, 100n * nominal)));
656 //
657 };
658
659 for (let i = 0; i < 40; i++) {
660 await crowdStakes();
661 }
662
663 // TODO pay rewards for some period
664 });
665 });
611});666});
612667
613async function waitForRelayBlock(api: ApiPromise, blocks = 1): Promise<void> {668async function waitForRelayBlock(api: ApiPromise, blocks = 1): Promise<void> {
modifiedtests/src/removeCollectionAdmin.test.tsdiffbeforeafterboth

no syntactic changes

modifiedtests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth
5import {UniqueHelper} from './unique';5import {UniqueHelper} from './unique';
6import {ApiPromise, WsProvider} from '@polkadot/api';6import {ApiPromise, WsProvider} from '@polkadot/api';
7import * as defs from '../../interfaces/definitions';7import * as defs from '../../interfaces/definitions';
8import {TSigner} from './types';
9import {IKeyringPair} from '@polkadot/types/types';8import {IKeyringPair} from '@polkadot/types/types';
109
1110
70 * @returns array of newly created accounts69 * @returns array of newly created accounts
71 * @example const [acc1, acc2, acc3] = await createAccounts([0n, 10n, 20n], donor); 70 * @example const [acc1, acc2, acc3] = await createAccounts([0n, 10n, 20n], donor);
72 */71 */
73 creteAccounts = async (balances: bigint[], donor: IKeyringPair): Promise<IKeyringPair[]> => {72 createAccounts = async (balances: bigint[], donor: IKeyringPair): Promise<IKeyringPair[]> => {
74 let nonce = await this.helper.chain.getNonce(donor.address);73 let nonce = await this.helper.chain.getNonce(donor.address);
75 const tokenNominal = this.helper.balance.getOneTokenNominal();74 const tokenNominal = this.helper.balance.getOneTokenNominal();
76 const transactions = [];75 const transactions = [];
103 let accountsCreated = false;102 let accountsCreated = false;
104 // checkBalances retry up to 5 blocks103 // checkBalances retry up to 5 blocks
105 for (let index = 0; index < 5; index++) {104 for (let index = 0; index < 5; index++) {
106 console.log(await this.helper.chain.getLatestBlockNumber());
107 accountsCreated = await checkBalances();105 accountsCreated = await checkBalances();
108 if(accountsCreated) break;106 if(accountsCreated) break;
109 await this.waitNewBlocks(1);107 await this.waitNewBlocks(1);
115 return accounts;113 return accounts;
116 };114 };
115
116 // TODO combine this method and createAccounts into one
117 createCrowd = async (accountsToCreate: number, withBalance: bigint, donor: IKeyringPair): Promise<IKeyringPair[]> => {
118 let transactions: any = [];
119 const accounts: IKeyringPair[] = [];
120
121 const createAsManyAsCan = async () => {
122 let nonce = await this.helper.chain.getNonce(donor.address);
123 const tokenNominal = this.helper.balance.getOneTokenNominal();
124 for (let i = 0; i < accountsToCreate; i++) {
125 if (i === 500) { // if there are too many accounts to create
126 await Promise.allSettled(transactions); // wait while first 500 (should be 100 for devnode) tx will be settled
127 transactions = []; //
128 nonce = await this.helper.chain.getNonce(donor.address); // update nonce
129 }
130 const recepient = this.helper.util.fromSeed(mnemonicGenerate());
131 accounts.push(recepient);
132 if (withBalance !== 0n) {
133 const tx = this.helper.constructApiCall('api.tx.balances.transfer', [{Id: recepient.address}, withBalance * tokenNominal]);
134 transactions.push(this.helper.signTransaction(donor, tx, 'account generation', {nonce}));
135 nonce++;
136 }
137 }
138
139 const fullfilledAccounts = [];
140 await Promise.allSettled(transactions);
141 for (const account of accounts) {
142 const accountBalance = await this.helper.balance.getSubstrate(account.address);
143 if (accountBalance === withBalance * tokenNominal) {
144 fullfilledAccounts.push(account);
145 }
146 }
147 return fullfilledAccounts;
148 };
149
150
151 const crowd: IKeyringPair[] = [];
152 // do up to 5 retries
153 for (let index = 0; index < 5 && accountsToCreate !== 0; index++) {
154 const asManyAsCan = await createAsManyAsCan();
155 crowd.push(...asManyAsCan);
156 accountsToCreate -= asManyAsCan.length;
157 }
158
159 if (accountsToCreate !== 0) throw Error(`Crowd generation failed: ${accountsToCreate} accounts left`);
160
161 return crowd;
162 };
163
164 isDevNode = async () => {
165 const block1 = await this.helper.api?.rpc.chain.getBlock(await this.helper.api?.rpc.chain.getBlockHash(1));
166 const block2 = await this.helper.api?.rpc.chain.getBlock(await this.helper.api?.rpc.chain.getBlockHash(2));
167 const findCreationDate = async (block: any) => {
168 const humanBlock = block.toHuman();
169 let date;
170 humanBlock.block.extrinsics.forEach((ext: any) => {
171 if(ext.method.section === 'timestamp') {
172 date = Number(ext.method.args.now.replaceAll(',', ''));
173 }
174 });
175 return date;
176 };
177 const block1date = await findCreationDate(block1);
178 const block2date = await findCreationDate(block2);
179 if(block2date! - block1date! < 9000) return true;
180 };
117181
118 /**182 /**
119 * Wait for specified bnumber of blocks183 * Wait for specified bnumber of blocks