difftreelog
Merge pull request #1017 from UniqueNetwork/feature/speedup-inflation-pallet
in: master
11 files changed
.docker/Dockerfile-chain-devdiffbeforeafterboth--- a/.docker/Dockerfile-chain-dev
+++ b/.docker/Dockerfile-chain-dev
@@ -21,7 +21,7 @@
WORKDIR /dev_chain
-RUN cargo build --profile integration-tests --features=${NETWORK}-runtime
+RUN cargo build --profile integration-tests --features=${NETWORK}-runtime,fast-inflation
RUN echo "$NETWORK"
-CMD cargo run --profile integration-tests --features=${NETWORK}-runtime -- --dev -linfo --rpc-cors=all --unsafe-rpc-external
+CMD cargo run --profile integration-tests --features=${NETWORK}-runtime,fast-inflation -- --dev -linfo --rpc-cors=all --unsafe-rpc-external
.docker/Dockerfile-uniquediffbeforeafterboth--- a/.docker/Dockerfile-unique
+++ b/.docker/Dockerfile-unique
@@ -47,7 +47,7 @@
--mount=type=cache,target=/unique_parachain/unique-chain/target \
cd unique-chain && \
echo "Using runtime features '$RUNTIME_FEATURES'" && \
- CARGO_INCREMENTAL=0 cargo build --profile integration-tests --features="$RUNTIME_FEATURES" --locked && \
+ CARGO_INCREMENTAL=0 cargo build --profile integration-tests --features=fast-inflation,"$RUNTIME_FEATURES" --locked && \
mv ./target/integration-tests/unique-collator /unique_parachain/unique-chain/ && \
cd target/integration-tests/wbuild && find . -name "*.wasm" -exec sh -c 'mkdir -p "../../../wasm/$(dirname {})"; cp {} "../../../wasm/{}"' \;
.docker/docker-compose.gov.j2diffbeforeafterboth--- a/.docker/docker-compose.gov.j2
+++ b/.docker/docker-compose.gov.j2
@@ -21,4 +21,4 @@
options:
max-size: "1m"
max-file: "3"
- command: cargo run --profile integration-tests --features={{ NETWORK }}-runtime,gov-test-timings -- --dev -linfo --rpc-cors=all --unsafe-rpc-external
+ command: cargo run --profile integration-tests --features={{ NETWORK }}-runtime,gov-test-timings,fast-inflation -- --dev -linfo --rpc-cors=all --unsafe-rpc-external
js-packages/tests/creditFeesToTreasury.seqtest.tsdiffbeforeafterboth--- a/js-packages/tests/creditFeesToTreasury.seqtest.ts
+++ b/js-packages/tests/creditFeesToTreasury.seqtest.ts
@@ -33,7 +33,7 @@
const blockInterval = inflationBlockInterval.toNumber();
const unsubscribe = await api.rpc.chain.subscribeNewHeads(head => {
const currentBlock = head.number.toNumber();
- if(currentBlock % blockInterval < blockInterval - 10) {
+ if(currentBlock % blockInterval < blockInterval - (blockInterval / 5)) {
unsubscribe();
resolve();
} else {
js-packages/tests/eth/util/playgrounds/unique.dev.tsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// SPDX-License-Identifier: Apache-2.034/* eslint-disable function-call-argument-newline */56import {readFile} from 'fs/promises';78import * as web3 from 'web3';9import {WebsocketProvider} from 'web3-core';10import {Contract} from 'web3-eth-contract';1112// @ts-ignore13import solc from 'solc';1415import {evmToAddress} from '@polkadot/util-crypto';16import type {IKeyringPair} from '@polkadot/types/types';1718import {ArrangeGroup, DevUniqueHelper} from '@unique/playgrounds/unique.dev.js';1920import type {ContractImports, CompiledContract, CrossAddress, NormalizedEvent, EthProperty} from './types.js';21import {CollectionMode, CreateCollectionData} from './types.js';2223// Native contracts ABI24import collectionHelpersAbi from '../../abi/collectionHelpers.json' assert {type: 'json'};25import nativeFungibleAbi from '../../abi/nativeFungible.json' assert {type: 'json'};26import fungibleAbi from '../../abi/fungible.json' assert {type: 'json'};27import fungibleDeprecatedAbi from '../../abi/fungibleDeprecated.json' assert {type: 'json'};28import nonFungibleAbi from '../../abi/nonFungible.json' assert {type: 'json'};29import nonFungibleDeprecatedAbi from '../../abi/nonFungibleDeprecated.json' assert {type: 'json'};30import refungibleAbi from '../../abi/reFungible.json' assert {type: 'json'};31import refungibleDeprecatedAbi from '../../abi/reFungibleDeprecated.json' assert {type: 'json'};32import refungibleTokenAbi from '../../abi/reFungibleToken.json' assert {type: 'json'};33import refungibleTokenDeprecatedAbi from '../../abi/reFungibleTokenDeprecated.json' assert {type: 'json'};34import contractHelpersAbi from '../../abi/contractHelpers.json' assert {type: 'json'};35import type {ICrossAccountId, TEthereumAccount, TCollectionMode} from '@unique/playgrounds/types.js';3637class EthGroupBase {38 helper: EthUniqueHelper;39 gasPrice?: string;4041 constructor(helper: EthUniqueHelper) {42 this.helper = helper;43 }44 async getGasPrice() {45 if(this.gasPrice)46 return this.gasPrice;47 this.gasPrice = await this.helper.getWeb3().eth.getGasPrice();48 return this.gasPrice;49 }50}5152class ContractGroup extends EthGroupBase {53 async findImports(imports?: ContractImports[]) {54 if(!imports) return function(path: string) {55 return {error: `File not found: ${path}`};56 };5758 const knownImports = {} as { [key: string]: string };59 for(const imp of imports) {60 knownImports[imp.solPath] = (await readFile(imp.fsPath)).toString();61 }6263 return function(path: string) {64 if(path in knownImports) return {contents: knownImports[path]};65 return {error: `File not found: ${path}`};66 };67 }6869 async compile(name: string, src: string, imports?: ContractImports[]): Promise<CompiledContract> {70 const compiled = JSON.parse(solc.compile(JSON.stringify({71 language: 'Solidity',72 sources: {73 [`${name}.sol`]: {74 content: src,75 },76 },77 settings: {78 outputSelection: {79 '*': {80 '*': ['*'],81 },82 },83 },84 }), {import: await this.findImports(imports)}));8586 const hasErrors = compiled['errors']87 && compiled['errors'].length > 088 && compiled.errors.some(function(err: any) {89 return err.severity == 'error';90 });9192 if(hasErrors) {93 throw compiled.errors;94 }95 const out = compiled.contracts[`${name}.sol`][name];9697 return {98 abi: out.abi,99 object: '0x' + out.evm.bytecode.object,100 };101 }102103 async deployByCode(signer: string, name: string, src: string, imports?: ContractImports[], gas?: number, args?: any[]): Promise<Contract> {104 const compiledContract = await this.compile(name, src, imports);105 return this.deployByAbi(signer, compiledContract.abi, compiledContract.object, gas, args);106 }107108 async deployByAbi(signer: string, abi: any, object: string, gas?: number, args?: any[]): Promise<Contract> {109 const web3 = this.helper.getWeb3();110 const contract = new web3.eth.Contract(abi, undefined, {111 data: object,112 from: signer,113 gas: gas ?? this.helper.eth.DEFAULT_GAS,114 });115 return await contract.deploy({data: object, arguments: args}).send({from: signer});116 }117118}119120class NativeContractGroup extends EthGroupBase {121122 contractHelpers(caller: string) {123 const web3 = this.helper.getWeb3();124 return new web3.eth.Contract(contractHelpersAbi as any, this.helper.getApi().consts.evmContractHelpers.contractAddress.toString(), {125 from: caller,126 gas: this.helper.eth.DEFAULT_GAS,127 });128 }129130 collectionHelpers(caller: string) {131 const web3 = this.helper.getWeb3();132 return new web3.eth.Contract(collectionHelpersAbi as any, this.helper.getApi().consts.common.contractAddress.toString(), {133 from: caller,134 gas: this.helper.eth.DEFAULT_GAS,135 });136 }137138 collection(address: string, mode: TCollectionMode, caller?: string, mergeDeprecated = false) {139 let abi;140 if(address === this.helper.ethAddress.fromCollectionId(0)) {141 abi = nativeFungibleAbi;142 } else {143 abi ={144 'nft': nonFungibleAbi,145 'rft': refungibleAbi,146 'ft': fungibleAbi,147 }[mode];148 }149 if(mergeDeprecated) {150 const deprecated = {151 'nft': nonFungibleDeprecatedAbi,152 'rft': refungibleDeprecatedAbi,153 'ft': fungibleDeprecatedAbi,154 }[mode];155 abi = [...abi, ...deprecated];156 }157 const web3 = this.helper.getWeb3();158 return new web3.eth.Contract(abi as any, address, {159 gas: this.helper.eth.DEFAULT_GAS,160 ...(caller ? {from: caller} : {}),161 });162 }163164 collectionById(collectionId: number, mode: 'nft' | 'rft' | 'ft', caller?: string, mergeDeprecated = false) {165 return this.collection(this.helper.ethAddress.fromCollectionId(collectionId), mode, caller, mergeDeprecated);166 }167168 rftToken(address: string, caller?: string, mergeDeprecated = false) {169 const web3 = this.helper.getWeb3();170 const abi = mergeDeprecated ? [...refungibleTokenAbi, ...refungibleTokenDeprecatedAbi] : refungibleTokenAbi;171 return new web3.eth.Contract(abi as any, address, {172 gas: this.helper.eth.DEFAULT_GAS,173 ...(caller ? {from: caller} : {}),174 });175 }176177 rftTokenById(collectionId: number, tokenId: number, caller?: string, mergeDeprecated = false) {178 return this.rftToken(this.helper.ethAddress.fromTokenId(collectionId, tokenId), caller, mergeDeprecated);179 }180}181182class CreateCollectionTransaction {183 signer: string;184 data: CreateCollectionData;185 mergeDeprecated: boolean;186 helper: EthUniqueHelper;187188 constructor(helper: EthUniqueHelper, signer: string, data: CreateCollectionData, mergeDeprecated = false) {189 this.helper = helper;190 this.signer = signer;191192 let flags = 0;193 // convert CollectionFlags to number and join them in one number194 if(!data.flags) {195 flags = 0;196 } else if(typeof data.flags == 'number') {197 flags = data.flags;198 } else {199 for(let i = 0; i < data.flags.length; i++){200 const flag = data.flags[i];201 flags = flags | flag;202 }203 }204 data.flags = flags;205206 this.data = data;207 this.mergeDeprecated = mergeDeprecated;208 }209210 // eslint-disable-next-line require-await211 private async createTransaction() {212 const collectionHelper = this.helper.ethNativeContract.collectionHelpers(this.signer);213 let collectionMode;214 switch (this.data.collectionMode) {215 case 'nft': collectionMode = CollectionMode.Nonfungible; break;216 case 'rft': collectionMode = CollectionMode.Refungible; break;217 case 'ft': collectionMode = CollectionMode.Fungible; break;218 }219220 const tx = collectionHelper.methods.createCollection([221 this.data.name,222 this.data.description,223 this.data.tokenPrefix,224 collectionMode,225 this.data.decimals,226 this.data.properties,227 this.data.tokenPropertyPermissions,228 this.data.adminList,229 this.data.nestingSettings,230 this.data.limits,231 this.data.pendingSponsor,232 this.data.flags,233 ]);234 return tx;235 }236237 async send(options?: any): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[], collection: Contract }> {238 const collectionCreationPrice = {239 value: Number(this.helper.balance.getCollectionCreationPrice()),240 };241 const tx = await this.createTransaction();242 const result = await tx.send({...options, ...collectionCreationPrice});243244 const collectionAddress = this.helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);245 const collectionId = this.helper.ethAddress.extractCollectionId(collectionAddress);246 const events = this.helper.eth.normalizeEvents(result.events);247 const collection = await this.helper.ethNativeContract.collectionById(collectionId, this.data.collectionMode, this.signer, this.mergeDeprecated);248249 return {collectionId, collectionAddress, events, collection};250 }251252 async call(options?: any) {253 const collectionCreationPrice = {254 value: Number(this.helper.balance.getCollectionCreationPrice()),255 };256 const tx = await this.createTransaction();257258 return await tx.call({...options, ...collectionCreationPrice});259 }260}261262263class EthGroup extends EthGroupBase {264 DEFAULT_GAS = 2_500_000;265266 createAccount() {267 const web3 = this.helper.getWeb3();268 const account = web3.eth.accounts.create();269 web3.eth.accounts.wallet.add(account.privateKey);270 return account.address;271 }272273 async createAccountWithBalance(donor: IKeyringPair, amount = 600n) {274 const account = this.createAccount();275 await this.transferBalanceFromSubstrate(donor, account, amount);276277 return account;278 }279280 async transferBalanceFromSubstrate(donor: IKeyringPair, recepient: string, amount = 100n, inTokens = true) {281 return await this.helper.balance.transferToSubstrate(donor, evmToAddress(recepient), amount * (inTokens ? this.helper.balance.getOneTokenNominal() : 1n));282 }283284 async getCollectionCreationFee(signer: string) {285 const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(signer);286 return await collectionHelper.methods.collectionCreationFee().call();287 }288289 async sendEVM(signer: IKeyringPair, contractAddress: string, abi: string, value: string, gasLimit?: number) {290 if(!gasLimit) gasLimit = this.DEFAULT_GAS;291 const web3 = this.helper.getWeb3();292 // FIXME: can't send legacy transaction using tx.evm.call293 const gasPrice = await web3.eth.getGasPrice();294 // TODO: check execution status295 await this.helper.executeExtrinsic(296 signer,297 'api.tx.evm.call', [this.helper.address.substrateToEth(signer.address), contractAddress, abi, value, gasLimit, gasPrice, null, null, []],298 true,299 );300 }301302 async callEVM(signer: TEthereumAccount, contractAddress: string, abi: string) {303 return await this.helper.callRpc('api.rpc.eth.call', [{from: signer, to: contractAddress, data: abi}]);304 }305306 createCollectionMethodName(mode: TCollectionMode) {307 switch (mode) {308 case 'ft':309 return 'createFTCollection';310 case 'nft':311 return 'createNFTCollection';312 case 'rft':313 return 'createRFTCollection';314 }315 }316317 createCollection(signer: string, data: CreateCollectionData, mergeDeprecated = false): CreateCollectionTransaction {318 return new CreateCollectionTransaction(this.helper, signer, data, mergeDeprecated);319 }320321 createNFTCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {322 return this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'nft')).send();323 }324325 async createERC721MetadataCompatibleCollection(signer: string, data: CreateCollectionData, baseUri: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {326 const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(signer);327328 const {collectionId, collectionAddress, events} = await this.createCollection(signer, data).send();329330 await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send();331332 return {collectionId, collectionAddress, events};333 }334335 async createERC721MetadataCompatibleNFTCollection(signer: string, name: string, description: string, tokenPrefix: string, baseUri: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {336 const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(signer);337338 const {collectionId, collectionAddress, events} = await this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'nft')).send();339340 await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send();341342 return {collectionId, collectionAddress, events};343 }344345 createRFTCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {346 return this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'rft')).send();347 }348349 createFungibleCollection(signer: string, name: string, _decimals: number, description: string, tokenPrefix: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {350 return this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'ft')).send();351 }352353 async createERC721MetadataCompatibleRFTCollection(signer: string, name: string, description: string, tokenPrefix: string, baseUri: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {354 const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(signer);355356 const {collectionId, collectionAddress, events} = await this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'rft')).send();357358 await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send();359360 return {collectionId, collectionAddress, events};361 }362363 async deployCollectorContract(signer: string): Promise<Contract> {364 return await this.helper.ethContract.deployByCode(signer, 'Collector', `365 // SPDX-License-Identifier: UNLICENSED366 pragma solidity ^0.8.6;367368 contract Collector {369 uint256 collected;370 fallback() external payable {371 giveMoney();372 }373 function giveMoney() public payable {374 collected += msg.value;375 }376 function getCollected() public view returns (uint256) {377 return collected;378 }379 function getUnaccounted() public view returns (uint256) {380 return address(this).balance - collected;381 }382383 function withdraw(address payable target) public {384 target.transfer(collected);385 collected = 0;386 }387 }388 `);389 }390391 async deployFlipper(signer: string): Promise<Contract> {392 return await this.helper.ethContract.deployByCode(signer, 'Flipper', `393 // SPDX-License-Identifier: UNLICENSED394 pragma solidity ^0.8.6;395396 contract Flipper {397 bool value = false;398 function flip() public {399 value = !value;400 }401 function getValue() public view returns (bool) {402 return value;403 }404 }405 `);406 }407408 async recordCallFee(user: string, call: () => Promise<any>): Promise<bigint> {409 const before = await this.helper.balance.getEthereum(user);410 await call();411 // In dev mode, the transaction might not finish processing in time412 await this.helper.wait.newBlocks(1);413 const after = await this.helper.balance.getEthereum(user);414415 return before - after;416 }417418 normalizeEvents(events: any): NormalizedEvent[] {419 const output = [];420 for(const key of Object.keys(events)) {421 if(key.match(/^[0-9]+$/)) {422 output.push(events[key]);423 } else if(Array.isArray(events[key])) {424 output.push(...events[key]);425 } else {426 output.push(events[key]);427 }428 }429 output.sort((a, b) => a.logIndex - b.logIndex);430 return output.map(({address, event, returnValues}) => {431 const args: { [key: string]: string } = {};432 for(const key of Object.keys(returnValues)) {433 if(!key.match(/^[0-9]+$/)) {434 args[key] = returnValues[key];435 }436 }437 return {438 address,439 event,440 args,441 };442 });443 }444445 async calculateFee(address: ICrossAccountId, code: () => Promise<any>): Promise<bigint> {446 const wrappedCode = async () => {447 await code();448 // In dev mode, the transaction might not finish processing in time449 await this.helper.wait.newBlocks(1);450 };451 return await this.helper.arrange.calculcateFee(address, wrappedCode);452 }453}454455class EthAddressGroup extends EthGroupBase {456 extractCollectionId(address: string): number {457 if(!(address.length === 42 || address.length === 40)) throw new Error('address wrong format');458 return parseInt(address.slice(address.length - 8), 16);459 }460461 fromCollectionId(collectionId: number): string {462 if(collectionId >= 0xffffffff || collectionId < 0) throw new Error('collectionId overflow');463 return (web3 as any).utils.toChecksumAddress(`0x17c4e6453cc49aaaaeaca894e6d9683e${collectionId.toString(16).padStart(8,'0')}`);464 }465466 extractTokenId(address: string): { collectionId: number, tokenId: number } {467 if(!address.startsWith('0x'))468 throw 'address not starts with "0x"';469 if(address.length > 42)470 throw 'address length is more than 20 bytes';471 return {472 collectionId: Number('0x' + address.substring(address.length - 16, address.length - 8)),473 tokenId: Number('0x' + address.substring(address.length - 8)),474 };475 }476477 fromTokenId(collectionId: number, tokenId: number): string {478 return this.helper.util.getTokenAddress({collectionId, tokenId});479 }480481 normalizeAddress(address: string): string {482 return '0x' + address.substring(address.length - 40);483 }484}485export class EthPropertyGroup extends EthGroupBase {486 property(key: string, value: string): EthProperty {487 return [488 key,489 '0x' + Buffer.from(value).toString('hex'),490 ];491 }492}493export type EthUniqueHelperConstructor = new (...args: any[]) => EthUniqueHelper;494495export class EthCrossAccountGroup extends EthGroupBase {496 createAccount(): CrossAddress {497 return this.fromAddress(this.helper.eth.createAccount());498 }499500 async createAccountWithBalance(donor: IKeyringPair, amount = 100n) {501 return this.fromAddress(await this.helper.eth.createAccountWithBalance(donor, amount));502 }503504 fromAddress(address: TEthereumAccount): CrossAddress {505 return {506 eth: address,507 sub: '0',508 };509 }510511 fromAddr(address: TEthereumAccount): [string, string] {512 return [513 address,514 '0',515 ];516 }517518 fromKeyringPair(keyring: IKeyringPair): CrossAddress {519 return {520 eth: '0x0000000000000000000000000000000000000000',521 sub: keyring.addressRaw,522 };523 }524}525526export class FeeGas {527 fee: number | bigint = 0n;528529 gas: number | bigint = 0n;530531 public static async build(helper: EthUniqueHelper, fee: bigint): Promise<FeeGas> {532 const instance = new FeeGas();533 instance.fee = instance.convertToTokens(fee);534 instance.gas = await instance.convertToGas(fee, helper);535 return instance;536 }537538 private async convertToGas(fee: bigint, helper: EthUniqueHelper): Promise<bigint> {539 const gasPrice = BigInt(await helper.getWeb3().eth.getGasPrice());540 return fee / gasPrice;541 }542543 private convertToTokens(value: bigint, nominal = 1_000_000_000_000_000_000n): number {544 return Number((value * 1000n) / nominal) / 1000;545 }546}547548class EthArrangeGroup extends ArrangeGroup {549 declare helper: EthUniqueHelper;550551 constructor(helper: EthUniqueHelper) {552 super(helper);553 this.helper = helper;554 }555556 async calculcateFeeGas(payer: ICrossAccountId, promise: () => Promise<any>): Promise<FeeGas> {557 const fee = await this.calculcateFee(payer, promise);558 return await FeeGas.build(this.helper, fee);559 }560}561export class EthUniqueHelper extends DevUniqueHelper {562 web3: web3.default | null = null;563 web3Provider: WebsocketProvider | null = null;564565 eth: EthGroup;566 ethAddress: EthAddressGroup;567 ethCrossAccount: EthCrossAccountGroup;568 ethNativeContract: NativeContractGroup;569 ethContract: ContractGroup;570 ethProperty: EthPropertyGroup;571 declare arrange: EthArrangeGroup;572 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: { [key: string]: any } = {}) {573 options.helperBase = options.helperBase ?? EthUniqueHelper;574575 super(logger, options);576 this.eth = new EthGroup(this);577 this.ethAddress = new EthAddressGroup(this);578 this.ethCrossAccount = new EthCrossAccountGroup(this);579 this.ethNativeContract = new NativeContractGroup(this);580 this.ethContract = new ContractGroup(this);581 this.ethProperty = new EthPropertyGroup(this);582 this.arrange = new EthArrangeGroup(this);583 super.arrange = this.arrange;584 }585586 getWeb3(): web3.default {587 if(this.web3 === null) throw Error('Web3 not connected');588 return this.web3;589 }590591 connectWeb3(wsEndpoint: string) {592 if(this.web3 !== null) return;593 this.web3Provider = new (web3 as any).providers.WebsocketProvider(wsEndpoint);594 this.web3 = new (web3 as any)(this.web3Provider);595 }596597 override async disconnect() {598 if(this.web3 === null) return;599 this.web3Provider?.connection.close();600601 await super.disconnect();602 }603604 override clearApi() {605 super.clearApi();606 this.web3 = null;607 }608609 override clone(helperCls: EthUniqueHelperConstructor, options?: { [key: string]: any; }): EthUniqueHelper {610 const newHelper = super.clone(helperCls, options) as EthUniqueHelper;611 newHelper.web3 = this.web3;612 newHelper.web3Provider = this.web3Provider;613614 return newHelper;615 }616}js-packages/tests/inflation.seqtest.tsdiffbeforeafterboth--- a/js-packages/tests/inflation.seqtest.ts
+++ b/js-packages/tests/inflation.seqtest.ts
@@ -17,13 +17,19 @@
import type {IKeyringPair} from '@polkadot/types/types';
import {expect, itSub, usingPlaygrounds} from './util/index.js';
+const TREASURY = '5EYCAe5ijiYfyeZ2JJCGq56LmPyNRAKzpG4QkoQkkQNB5e6Z';
+
// todo:playgrounds requires sudo, look into on the later stage
describe('integration test: Inflation', () => {
let superuser: IKeyringPair;
before(async () => {
- await usingPlaygrounds(async (_, privateKey) => {
+ await usingPlaygrounds(async (helper, privateKey) => {
superuser = await privateKey('//Alice');
+ const api = helper.getApi();
+
+ const relayBlock = (await api.query.parachainSystem.lastRelayChainBlockNumber()).toNumber();
+ await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [helper.constructApiCall('api.tx.inflation.startInflation', [relayBlock])])).to.not.be.rejected;
});
});
@@ -36,10 +42,6 @@
// Make sure superuser can't start inflation without explicit sudo
await expect(helper.executeExtrinsic(superuser, 'api.tx.inflation.startInflation', [1])).to.be.rejectedWith(/BadOrigin/);
- // Start inflation on relay block 1 (Alice is sudo)
- const tx = helper.constructApiCall('api.tx.inflation.startInflation', [1]);
- await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [tx])).to.not.be.rejected;
-
const blockInterval = (helper.getApi().consts.inflation.inflationBlockInterval as any).toBigInt();
const totalIssuanceStart = ((await helper.callRpc('api.query.inflation.startingYearTotalIssuance', [])) as any).toBigInt();
const blockInflation = (await helper.callRpc('api.query.inflation.blockInflation', []) as any).toBigInt();
@@ -55,4 +57,22 @@
expect(Math.abs(Number(expectedInflation))).to.be.lessThanOrEqual(tolerance);
});
+
+ itSub('Inflation happens after inflation block interval', async ({helper}) => {
+ const api = helper.getApi();
+ const blockInterval = await api.consts.inflation.inflationBlockInterval.toNumber();
+
+ const relayBlock = (await api.query.parachainSystem.lastRelayChainBlockNumber()).toNumber();
+ const blockInflation = (await helper.callRpc('api.query.inflation.blockInflation', []) as any).toBigInt();
+ const startBlock = (relayBlock + blockInterval) - (relayBlock % blockInterval) + 1;
+
+ await helper.wait.forRelayBlockNumber(startBlock);
+
+ const treasuryBalanceBefore = await helper.balance.getSubstrate(TREASURY);
+
+ await helper.wait.forRelayBlockNumber(startBlock + blockInterval + 1);
+
+ const treasuryBalanceAfter = await helper.balance.getSubstrate(TREASURY);
+ expect(Number(treasuryBalanceAfter)).to.be.eqls(Number(treasuryBalanceBefore + blockInflation));
+ });
});
pallets/inflation/Cargo.tomldiffbeforeafterboth--- a/pallets/inflation/Cargo.toml
+++ b/pallets/inflation/Cargo.toml
@@ -27,6 +27,7 @@
'sp-std/std',
]
try-runtime = ["frame-support/try-runtime"]
+fast-inflation = []
[dependencies]
parity-scale-codec = { workspace = true }
runtime/common/config/pallets/mod.rsdiffbeforeafterboth--- a/runtime/common/config/pallets/mod.rs
+++ b/runtime/common/config/pallets/mod.rs
@@ -101,8 +101,14 @@
type WeightInfo = pallet_balances::weights::SubstrateWeight<Self>;
}
+// every time per how many blocks inflation is applied
+#[cfg(feature = "fast-inflation")]
+parameter_types! {
+ pub const InflationBlockInterval: BlockNumber = 10;
+}
+#[cfg(not(feature = "fast-inflation"))]
parameter_types! {
- pub const InflationBlockInterval: BlockNumber = 100; // every time per how many blocks inflation is applied
+ pub const InflationBlockInterval: BlockNumber = 100;
}
/// Pallet-inflation needs block number in on_initialize, where there is no `validation_data` exists yet
runtime/opal/Cargo.tomldiffbeforeafterboth--- a/runtime/opal/Cargo.toml
+++ b/runtime/opal/Cargo.toml
@@ -232,6 +232,7 @@
preimage = []
refungible = []
session-test-timings = []
+fast-inflation = []
################################################################################
# local dependencies
runtime/quartz/Cargo.tomldiffbeforeafterboth--- a/runtime/quartz/Cargo.toml
+++ b/runtime/quartz/Cargo.toml
@@ -221,6 +221,7 @@
preimage = []
refungible = []
session-test-timings = []
+fast-inflation = []
################################################################################
# local dependencies
runtime/unique/Cargo.tomldiffbeforeafterboth--- a/runtime/unique/Cargo.toml
+++ b/runtime/unique/Cargo.toml
@@ -224,6 +224,7 @@
preimage = []
refungible = []
session-test-timings = []
+fast-inflation = []
################################################################################
# local dependencies