1234import {stringToU8a} from '@polkadot/util';5import {blake2AsHex, encodeAddress, mnemonicGenerate} from '@polkadot/util-crypto';6import {UniqueHelper, ChainHelperBase, ChainHelperBaseConstructor, HelperGroup, UniqueHelperConstructor} from './unique';7import {ApiPromise, Keyring, WsProvider} from '@polkadot/api';8import * as defs from '../../interfaces/definitions';9import {IKeyringPair} from '@polkadot/types/types';10import {EventRecord} from '@polkadot/types/interfaces';11import {ICrossAccountId, ILogger, IPovInfo, ISchedulerOptions, ITransactionResult, TSigner} from './types';12import {FrameSystemEventRecord, XcmV2TraitsError, XcmV3TraitsOutcome} from '@polkadot/types/lookup';13import {SignerOptions, VoidFn} from '@polkadot/api/types';14import {Pallets} from '..';15import {spawnSync} from 'child_process';16import {AcalaHelper, AstarHelper, MoonbeamHelper, PolkadexHelper, RelayHelper, WestmintHelper, ForeignAssetsGroup, XcmGroup, XTokensGroup, TokensGroup} from './unique.xcm';17import {CollectiveGroup, CollectiveMembershipGroup, DemocracyGroup, ICollectiveGroup, IFellowshipGroup, RankedCollectiveGroup, ReferendaGroup} from './unique.governance';1819export class SilentLogger {20 log(_msg: any, _level: any): void { }21 level = {22 ERROR: 'ERROR' as const,23 WARNING: 'WARNING' as const,24 INFO: 'INFO' as const,25 };26}2728export class SilentConsole {29 30 31 consoleErr: any;32 consoleLog: any;33 consoleWarn: any;3435 constructor() {36 this.consoleErr = console.error;37 this.consoleLog = console.log;38 this.consoleWarn = console.warn;39 }4041 enable() {42 const outFn = (printer: any) => (...args: any[]) => {43 for(const arg of args) {44 if(typeof arg !== 'string')45 continue;46 const skippedWarnings = ['1000:: Normal connection closure', 'Not decorating unknown runtime apis:', 'RPC methods not decorated:', 'Not decorating runtime apis', 'Bad input data provided to validate_transaction', 'account balance too low', '1006:: Abnormal Closure'];47 const needToSkip = skippedWarnings.reduce((a, b) => a || arg.includes(b), false);48 if(needToSkip || arg === 'Normal connection closure')49 return;50 }51 printer(...args);52 };5354 console.error = outFn(this.consoleErr.bind(console));55 console.log = outFn(this.consoleLog.bind(console));56 console.warn = outFn(this.consoleWarn.bind(console));57 }5859 disable() {60 console.error = this.consoleErr;61 console.log = this.consoleLog;62 console.warn = this.consoleWarn;63 }64}6566export interface IEventHelper {67 section(): string;6869 method(): string;7071 wrapEvent(data: any[]): any;72}737475function EventHelper(section: string, method: string, wrapEvent: (data: any[]) => any) {76 const helperClass = class implements IEventHelper {77 wrapEvent: (data: any[]) => any;78 _section: string;79 _method: string;8081 constructor() {82 this.wrapEvent = wrapEvent;83 this._section = section;84 this._method = method;85 }8687 section(): string {88 return this._section;89 }9091 method(): string {92 return this._method;93 }9495 filter(txres: ITransactionResult) {96 return txres.result.events.filter(e => e.event.section === section && e.event.method === method)97 .map(e => this.wrapEvent(e.event.data));98 }99100 find(txres: ITransactionResult) {101 const e = txres.result.events.find(e => e.event.section === section && e.event.method === method);102 return e ? this.wrapEvent(e.event.data) : null;103 }104105 expect(txres: ITransactionResult) {106 const e = this.find(txres);107 if(e) {108 return e;109 } else {110 throw Error(`Expected event ${section}.${method}`);111 }112 }113 };114115 return helperClass;116}117118function eventJsonData<T = any>(data: any[], index: number) {119 return data[index].toJSON() as T;120}121122function eventHumanData(data: any[], index: number) {123 return data[index].toHuman();124}125126function eventData<T = any>(data: any[], index: number) {127 return data[index] as T;128}129130131function EventSection(section: string) {132 return class Section {133 static section = section;134135 static Method(name: string, wrapEvent: (data: any[]) => any = () => {}) {136 const helperClass = EventHelper(Section.section, name, wrapEvent);137 return new helperClass();138 }139 };140}141142function schedulerSection(schedulerInstance: string) {143 return class extends EventSection(schedulerInstance) {144 static Dispatched = this.Method('Dispatched', data => ({145 task: eventJsonData(data, 0),146 id: eventHumanData(data, 1),147 result: data[2],148 }));149150 static PriorityChanged = this.Method('PriorityChanged', data => ({151 task: eventJsonData(data, 0),152 priority: eventJsonData(data, 1),153 }));154 };155}156157export class Event {158 static Democracy = class extends EventSection('democracy') {159 static Proposed = this.Method('Proposed', data => ({160 proposalIndex: eventJsonData<number>(data, 0),161 }));162163 static ExternalTabled = this.Method('ExternalTabled');164165 static Started = this.Method('Started', data => ({166 referendumIndex: eventJsonData<number>(data, 0),167 threshold: eventHumanData(data, 1),168 }));169170 static Voted = this.Method('Voted', data => ({171 voter: eventJsonData(data, 0),172 referendumIndex: eventJsonData<number>(data, 1),173 vote: eventJsonData(data, 2),174 }));175176 static Passed = this.Method('Passed', data => ({177 referendumIndex: eventJsonData<number>(data, 0),178 }));179180 static ProposalCanceled = this.Method('ProposalCanceled', data => ({181 propIndex: eventJsonData<number>(data, 0),182 }));183184 static Cancelled = this.Method('Cancelled', data => ({185 propIndex: eventJsonData<number>(data, 0),186 }));187188 static Vetoed = this.Method('Vetoed', data => ({189 who: eventHumanData(data, 0),190 proposalHash: eventHumanData(data, 1),191 until: eventJsonData<number>(data, 1),192 }));193 };194195 static Council = class extends EventSection('council') {196 static Proposed = this.Method('Proposed', data => ({197 account: eventHumanData(data, 0),198 proposalIndex: eventJsonData<number>(data, 1),199 proposalHash: eventHumanData(data, 2),200 threshold: eventJsonData<number>(data, 3),201 }));202 static Closed = this.Method('Closed', data => ({203 proposalHash: eventHumanData(data, 0),204 yes: eventJsonData<number>(data, 1),205 no: eventJsonData<number>(data, 2),206 }));207 static Executed = this.Method('Executed', data => ({208 proposalHash: eventHumanData(data, 0),209 }));210 };211212 static TechnicalCommittee = class extends EventSection('technicalCommittee') {213 static Proposed = this.Method('Proposed', data => ({214 account: eventHumanData(data, 0),215 proposalIndex: eventJsonData<number>(data, 1),216 proposalHash: eventHumanData(data, 2),217 threshold: eventJsonData<number>(data, 3),218 }));219 static Closed = this.Method('Closed', data => ({220 proposalHash: eventHumanData(data, 0),221 yes: eventJsonData<number>(data, 1),222 no: eventJsonData<number>(data, 2),223 }));224 static Approved = this.Method('Approved', data => ({225 proposalHash: eventHumanData(data, 0),226 }));227 static Executed = this.Method('Executed', data => ({228 proposalHash: eventHumanData(data, 0),229 result: eventHumanData(data, 1),230 }));231 };232233 static FellowshipReferenda = class extends EventSection('fellowshipReferenda') {234 static Submitted = this.Method('Submitted', data => ({235 referendumIndex: eventJsonData<number>(data, 0),236 trackId: eventJsonData<number>(data, 1),237 proposal: eventJsonData(data, 2),238 }));239240 static Cancelled = this.Method('Cancelled', data => ({241 index: eventJsonData<number>(data, 0),242 tally: eventJsonData(data, 1),243 }));244 };245246 static UniqueScheduler = schedulerSection('uniqueScheduler');247 static Scheduler = schedulerSection('scheduler');248249 static XcmpQueue = class extends EventSection('xcmpQueue') {250 static XcmpMessageSent = this.Method('XcmpMessageSent', data => ({251 messageHash: eventJsonData(data, 0),252 }));253254 static Success = this.Method('Success', data => ({255 messageHash: eventJsonData(data, 0),256 }));257258 static Fail = this.Method('Fail', data => ({259 messageHash: eventJsonData(data, 0),260 outcome: eventData<XcmV2TraitsError>(data, 2),261 }));262 };263264 static DmpQueue = class extends EventSection('dmpQueue') {265 static ExecutedDownward = this.Method('ExecutedDownward', data => ({266 outcome: eventData<XcmV3TraitsOutcome>(data, 2),267 }));268 };269}270271272export function SudoHelper<T extends ChainHelperBaseConstructor>(Base: T) {273 return class extends Base {274 constructor(...args: any[]) {275 super(...args);276 }277278 async executeExtrinsic(279 sender: IKeyringPair,280 extrinsic: string,281 params: any[],282 expectSuccess?: boolean,283 options: Partial<SignerOptions> | null = null,284 ): Promise<ITransactionResult> {285 const call = this.constructApiCall(extrinsic, params);286 const result = await super.executeExtrinsic(287 sender,288 'api.tx.sudo.sudo',289 [call],290 expectSuccess,291 options,292 );293294 if(result.status === 'Fail') return result;295296 const data = (result.result.events.find(x => x.event.section == 'sudo' && x.event.method == 'Sudid')?.event.data as any).sudoResult;297 if(data.isErr) {298 if(data.asErr.isModule) {299 const error = (result.result.events[1].event.data as any).sudoResult.asErr.asModule;300 const metaError = super.getApi()?.registry.findMetaError(error);301 throw new Error(`${metaError.section}.${metaError.name}`);302 } else if(data.asErr.isToken) {303 throw new Error(`Token: ${data.asErr.asToken}`);304 }305 306 throw new Error(`Misc: ${data.asErr.toHuman()}`);307 }308 return result;309 }310 async executeExtrinsicUncheckedWeight(311 sender: IKeyringPair,312 extrinsic: string,313 params: any[],314 expectSuccess?: boolean,315 options: Partial<SignerOptions> | null = null,316 ): Promise<ITransactionResult> {317 const call = this.constructApiCall(extrinsic, params);318 const result = await super.executeExtrinsic(319 sender,320 'api.tx.sudo.sudoUncheckedWeight',321 [call, {refTime: 0, proofSize: 0}],322 expectSuccess,323 options,324 );325326 if(result.status === 'Fail') return result;327328 const data = (result.result.events.find(x => x.event.section == 'sudo' && x.event.method == 'Sudid')?.event.data as any).sudoResult;329 if(data.isErr) {330 if(data.asErr.isModule) {331 const error = (result.result.events[1].event.data as any).sudoResult.asErr.asModule;332 const metaError = super.getApi()?.registry.findMetaError(error);333 throw new Error(`${metaError.section}.${metaError.name}`);334 } else if(data.asErr.isToken) {335 throw new Error(`Token: ${data.asErr.asToken}`);336 }337 338 throw new Error(`Misc: ${data.asErr.toHuman()}`);339 }340 return result;341 }342 };343}344345class SchedulerGroup extends HelperGroup<UniqueHelper> {346 constructor(helper: UniqueHelper) {347 super(helper);348 }349350 cancelScheduled(signer: TSigner, scheduledId: string) {351 return this.helper.executeExtrinsic(352 signer,353 'api.tx.scheduler.cancelNamed',354 [scheduledId],355 true,356 );357 }358359 changePriority(signer: TSigner, scheduledId: string, priority: number) {360 return this.helper.executeExtrinsic(361 signer,362 'api.tx.scheduler.changeNamedPriority',363 [scheduledId, priority],364 true,365 );366 }367368 scheduleAt<T extends DevUniqueHelper>(369 executionBlockNumber: number,370 options: ISchedulerOptions = {},371 ) {372 return this.schedule<T>('schedule', executionBlockNumber, options);373 }374375 scheduleAfter<T extends DevUniqueHelper>(376 blocksBeforeExecution: number,377 options: ISchedulerOptions = {},378 ) {379 return this.schedule<T>('scheduleAfter', blocksBeforeExecution, options);380 }381382 schedule<T extends UniqueHelper>(383 scheduleFn: 'schedule' | 'scheduleAfter',384 blocksNum: number,385 options: ISchedulerOptions = {},386 ) {387 388 const ScheduledHelperType = ScheduledUniqueHelper(this.helper.helperBase);389 return this.helper.clone(ScheduledHelperType, {390 scheduleFn,391 blocksNum,392 options,393 }) as T;394 }395}396397class CollatorSelectionGroup extends HelperGroup<UniqueHelper> {398 399 addInvulnerable(signer: TSigner, address: string) {400 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.addInvulnerable', [address]);401 }402403 removeInvulnerable(signer: TSigner, address: string) {404 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.removeInvulnerable', [address]);405 }406407 async getInvulnerables(): Promise<string[]> {408 return (await this.helper.callRpc('api.query.collatorSelection.invulnerables')).map((x: any) => x.toHuman());409 }410411 412 maxCollators(): number {413 return (this.helper.getApi().consts.configuration.defaultCollatorSelectionMaxCollators.toJSON() as number);414 }415416 async getDesiredCollators(): Promise<number> {417 return (await this.helper.callRpc('api.query.configuration.collatorSelectionDesiredCollatorsOverride')).toNumber();418 }419420 setLicenseBond(signer: TSigner, amount: bigint) {421 return this.helper.executeExtrinsic(signer, 'api.tx.configuration.setCollatorSelectionLicenseBond', [amount]);422 }423424 async getLicenseBond(): Promise<bigint> {425 return (await this.helper.callRpc('api.query.configuration.collatorSelectionLicenseBondOverride')).toBigInt();426 }427428 obtainLicense(signer: TSigner) {429 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.getLicense', []);430 }431432 releaseLicense(signer: TSigner) {433 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.releaseLicense', []);434 }435436 forceReleaseLicense(signer: TSigner, released: string) {437 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.forceReleaseLicense', [released]);438 }439440 async hasLicense(address: string): Promise<bigint> {441 return (await this.helper.callRpc('api.query.collatorSelection.licenseDepositOf', [address])).toBigInt();442 }443444 onboard(signer: TSigner) {445 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.onboard', []);446 }447448 offboard(signer: TSigner) {449 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.offboard', []);450 }451452 async getCandidates(): Promise<string[]> {453 return (await this.helper.callRpc('api.query.collatorSelection.candidates')).map((x: any) => x.toHuman());454 }455}456457export class DevUniqueHelper extends UniqueHelper {458 459460461 arrange: ArrangeGroup;462 wait: WaitGroup;463 admin: AdminGroup;464 session: SessionGroup;465 testUtils: TestUtilGroup;466 foreignAssets: ForeignAssetsGroup;467 xcm: XcmGroup<DevUniqueHelper>;468 xTokens: XTokensGroup<DevUniqueHelper>;469 tokens: TokensGroup<DevUniqueHelper>;470 scheduler: SchedulerGroup;471 collatorSelection: CollatorSelectionGroup;472 council: ICollectiveGroup;473 technicalCommittee: ICollectiveGroup;474 fellowship: IFellowshipGroup;475 democracy: DemocracyGroup;476477 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {478 options.helperBase = options.helperBase ?? DevUniqueHelper;479480 super(logger, options);481 this.arrange = new ArrangeGroup(this);482 this.wait = new WaitGroup(this);483 this.admin = new AdminGroup(this);484 this.testUtils = new TestUtilGroup(this);485 this.session = new SessionGroup(this);486 this.foreignAssets = new ForeignAssetsGroup(this);487 this.xcm = new XcmGroup(this, 'polkadotXcm');488 this.xTokens = new XTokensGroup(this);489 this.tokens = new TokensGroup(this);490 this.scheduler = new SchedulerGroup(this);491 this.collatorSelection = new CollatorSelectionGroup(this);492 this.council = {493 collective: new CollectiveGroup(this, 'council'),494 membership: new CollectiveMembershipGroup(this, 'councilMembership'),495 };496 this.technicalCommittee = {497 collective: new CollectiveGroup(this, 'technicalCommittee'),498 membership: new CollectiveMembershipGroup(this, 'technicalCommitteeMembership'),499 };500 this.fellowship = {501 collective: new RankedCollectiveGroup(this, 'fellowshipCollective'),502 referenda: new ReferendaGroup(this, 'fellowshipReferenda'),503 };504 this.democracy = new DemocracyGroup(this);505 }506507 async connect(wsEndpoint: string, _listeners?: any): Promise<void> {508 if(!wsEndpoint) throw new Error('wsEndpoint was not set');509 const wsProvider = new WsProvider(wsEndpoint);510 this.api = new ApiPromise({511 provider: wsProvider,512 signedExtensions: {513 ContractHelpers: {514 extrinsic: {},515 payload: {},516 },517 CheckMaintenance: {518 extrinsic: {},519 payload: {},520 },521 DisableIdentityCalls: {522 extrinsic: {},523 payload: {},524 },525 FakeTransactionFinalizer: {526 extrinsic: {},527 payload: {},528 },529 },530 rpc: {531 unique: defs.unique.rpc,532 appPromotion: defs.appPromotion.rpc,533 povinfo: defs.povinfo.rpc,534 eth: {535 feeHistory: {536 description: 'Dummy',537 params: [],538 type: 'u8',539 },540 maxPriorityFeePerGas: {541 description: 'Dummy',542 params: [],543 type: 'u8',544 },545 },546 },547 });548 await this.api.isReadyOrError;549 this.network = await UniqueHelper.detectNetwork(this.api);550 this.wsEndpoint = wsEndpoint;551 }552 getSudo<T extends DevUniqueHelper>() {553 554 const SudoHelperType = SudoHelper(this.helperBase);555 return this.clone(SudoHelperType) as T;556 }557}558559export class DevRelayHelper extends RelayHelper {560 wait: WaitGroup;561562 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {563 options.helperBase = options.helperBase ?? DevRelayHelper;564565 super(logger, options);566 this.wait = new WaitGroup(this);567 }568569 getSudo() {570 571 const SudoHelperType = SudoHelper(this.helperBase);572 return this.clone(SudoHelperType) as DevRelayHelper;573 }574}575576export class DevWestmintHelper extends WestmintHelper {577 wait: WaitGroup;578579 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {580 options.helperBase = options.helperBase ?? DevWestmintHelper;581582 super(logger, options);583 this.wait = new WaitGroup(this);584 }585}586587export class DevStatemineHelper extends DevWestmintHelper {}588589export class DevStatemintHelper extends DevWestmintHelper {}590591export class DevMoonbeamHelper extends MoonbeamHelper {592 account: MoonbeamAccountGroup;593 wait: WaitGroup;594 fastDemocracy: MoonbeamFastDemocracyGroup;595596 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {597 options.helperBase = options.helperBase ?? DevMoonbeamHelper;598 options.notePreimagePallet = options.notePreimagePallet ?? 'preimage';599600 super(logger, options);601 this.account = new MoonbeamAccountGroup(this);602 this.wait = new WaitGroup(this);603 this.fastDemocracy = new MoonbeamFastDemocracyGroup(this);604 }605}606607export class DevMoonriverHelper extends DevMoonbeamHelper {608 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {609 options.notePreimagePallet = options.notePreimagePallet ?? 'preimage';610 super(logger, options);611 }612}613614export class DevAstarHelper extends AstarHelper {615 wait: WaitGroup;616617 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {618 options.helperBase = options.helperBase ?? DevAstarHelper;619620 super(logger, options);621 this.wait = new WaitGroup(this);622 }623624 getSudo<T extends AstarHelper>() {625 626 const SudoHelperType = SudoHelper(this.helperBase);627 return this.clone(SudoHelperType) as T;628 }629}630631export class DevShidenHelper extends DevAstarHelper { }632633export class DevAcalaHelper extends AcalaHelper {634 wait: WaitGroup;635636 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {637 options.helperBase = options.helperBase ?? DevAcalaHelper;638639 super(logger, options);640 this.wait = new WaitGroup(this);641 }642 getSudo() {643 644 const SudoHelperType = SudoHelper(this.helperBase);645 return this.clone(SudoHelperType) as DevAcalaHelper;646 }647}648649export class DevPolkadexHelper extends PolkadexHelper {650 wait: WaitGroup;651 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {652 options.helperBase = options.helperBase ?? PolkadexHelper;653654 super(logger, options);655 this.wait = new WaitGroup(this);656 }657658 getSudo() {659 660 const SudoHelperType = SudoHelper(this.helperBase);661 return this.clone(SudoHelperType) as DevPolkadexHelper;662 }663}664665export class DevKaruraHelper extends DevAcalaHelper {}666667export class ArrangeGroup {668 helper: DevUniqueHelper;669670 scheduledIdSlider = 0;671672 constructor(helper: DevUniqueHelper) {673 this.helper = helper;674 }675676 677678679680681682683 createAccounts = async (balances: bigint[], donor: IKeyringPair): Promise<IKeyringPair[]> => {684 let nonce = await this.helper.chain.getNonce(donor.address);685 const wait = new WaitGroup(this.helper);686 const ss58Format = this.helper.chain.getChainProperties().ss58Format;687 const tokenNominal = this.helper.balance.getOneTokenNominal();688 const transactions = [];689 const accounts: IKeyringPair[] = [];690 for(const balance of balances) {691 const recipient = this.helper.util.fromSeed(mnemonicGenerate(), ss58Format);692 accounts.push(recipient);693 if(balance !== 0n) {694 const tx = this.helper.constructApiCall('api.tx.balances.transfer', [{Id: recipient.address}, balance * tokenNominal]);695 transactions.push(this.helper.signTransaction(donor, tx, {nonce, era: 0}, 'account generation'));696 nonce++;697 }698 }699700 await Promise.all(transactions).catch(_e => {});701702 703 const checkBalances = async () => {704 let isSuccess = true;705 for(let i = 0; i < balances.length; i++) {706 const balance = await this.helper.balance.getSubstrate(accounts[i].address);707 if(balance !== balances[i] * tokenNominal) {708 isSuccess = false;709 break;710 }711 }712 return isSuccess;713 };714715 let accountsCreated = false;716 const maxBlocksChecked = await this.helper.arrange.isDevNode() ? 50 : 5;717 718 for(let index = 0; index < maxBlocksChecked; index++) {719 accountsCreated = await checkBalances();720 if(accountsCreated) break;721 await wait.newBlocks(1);722 }723724 if(!accountsCreated) throw Error('Accounts generation failed');725 726727 return accounts;728 };729730 731 createCrowd = async (accountsToCreate: number, withBalance: bigint, donor: IKeyringPair): Promise<IKeyringPair[]> => {732 const createAsManyAsCan = async () => {733 let transactions: any = [];734 const accounts: IKeyringPair[] = [];735 let nonce = await this.helper.chain.getNonce(donor.address);736 const tokenNominal = this.helper.balance.getOneTokenNominal();737 const ss58Format = this.helper.chain.getChainProperties().ss58Format;738 for(let i = 0; i < accountsToCreate; i++) {739 if(i === 500) { 740 await Promise.allSettled(transactions); 741 transactions = []; 742 nonce = await this.helper.chain.getNonce(donor.address); 743 }744 const recipient = this.helper.util.fromSeed(mnemonicGenerate(), ss58Format);745 accounts.push(recipient);746 if(withBalance !== 0n) {747 const tx = this.helper.constructApiCall('api.tx.balances.transfer', [{Id: recipient.address}, withBalance * tokenNominal]);748 transactions.push(this.helper.signTransaction(donor, tx, {nonce}, 'account generation'));749 nonce++;750 }751 }752753 const fullfilledAccounts = [];754 await Promise.allSettled(transactions);755 for(const account of accounts) {756 const accountBalance = await this.helper.balance.getSubstrate(account.address);757 if(accountBalance === withBalance * tokenNominal) {758 fullfilledAccounts.push(account);759 }760 }761 return fullfilledAccounts;762 };763764765 const crowd: IKeyringPair[] = [];766 767 for(let index = 0; index < 5 && accountsToCreate !== 0; index++) {768 const asManyAsCan = await createAsManyAsCan();769 crowd.push(...asManyAsCan);770 accountsToCreate -= asManyAsCan.length;771 }772773 if(accountsToCreate !== 0) throw Error(`Crowd generation failed: ${accountsToCreate} accounts left`);774775 return crowd;776 };777778 779780781782783 createEmptyAccount = (): IKeyringPair => {784 const ss58Format = this.helper.chain.getChainProperties().ss58Format;785 return this.helper.util.fromSeed(mnemonicGenerate(), ss58Format);786 };787788 isDevNode = async () => {789 let blockNumber = (await this.helper.callRpc('api.query.system.number')).toJSON();790 if(blockNumber == 0) {791 await this.helper.wait.newBlocks(1);792 blockNumber = (await this.helper.callRpc('api.query.system.number')).toJSON();793 }794 const block2 = await this.helper.callRpc('api.rpc.chain.getBlock', [await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])]);795 const block1 = await this.helper.callRpc('api.rpc.chain.getBlock', [await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber - 1])]);796 const findCreationDate = (block: any) => {797 const humanBlock = block.toHuman();798 let date;799 humanBlock.block.extrinsics.forEach((ext: any) => {800 if(ext.method.section === 'timestamp') {801 date = Number(ext.method.args.now.replaceAll(',', ''));802 }803 });804 return date;805 };806 const block1date = await findCreationDate(block1);807 const block2date = await findCreationDate(block2);808 if(block2date! - block1date! < 9000) return true;809 };810811 async calculcateFee(payer: ICrossAccountId, promise: () => Promise<any>): Promise<bigint> {812 const address = 'Substrate' in payer ? payer.Substrate : this.helper.address.ethToSubstrate(payer.Ethereum);813 let balance = await this.helper.balance.getSubstrate(address);814815 await promise();816817 balance -= await this.helper.balance.getSubstrate(address);818819 return balance;820 }821822 async calculatePoVInfo(txs: any[]): Promise<IPovInfo> {823 const rawPovInfo = await this.helper.callRpc('api.rpc.povinfo.estimateExtrinsicPoV', [txs]);824825 const kvJson: {[key: string]: string} = {};826827 for(const kv of rawPovInfo.keyValues) {828 kvJson[kv.key.toHex()] = kv.value.toHex();829 }830831 const kvStr = JSON.stringify(kvJson);832833 const chainql = spawnSync(834 'chainql',835 [836 `--tla-code=data=${kvStr}`,837 '-e', `function(data) cql.dump(cql.chain("${this.helper.getEndpoint()}").latest._meta, data, {omit_empty:true})`,838 ],839 );840841 if(!chainql.stdout) {842 throw Error('unable to get an output from the `chainql`');843 }844845 return {846 proofSize: rawPovInfo.proofSize.toNumber(),847 compactProofSize: rawPovInfo.compactProofSize.toNumber(),848 compressedProofSize: rawPovInfo.compressedProofSize.toNumber(),849 results: rawPovInfo.results,850 kv: JSON.parse(chainql.stdout.toString()),851 };852 }853854 calculatePalletAddress(palletId: any) {855 const address = stringToU8a(('modl' + palletId).padEnd(32, '\0'));856 return encodeAddress(address, this.helper.chain.getChainProperties().ss58Format);857 }858859 makeScheduledIds(num: number): string[] {860 function makeId(slider: number) {861 const scheduledIdSize = 64;862 const hexId = slider.toString(16);863 const prefixSize = scheduledIdSize - hexId.length;864865 const scheduledId = '0x' + '0'.repeat(prefixSize) + hexId;866867 return scheduledId;868 }869870 const ids = [];871 for(let i = 0; i < num; i++) {872 ids.push(makeId(this.scheduledIdSlider));873 this.scheduledIdSlider += 1;874 }875876 return ids;877 }878879 makeScheduledId(): string {880 return (this.makeScheduledIds(1))[0];881 }882883 async captureEvents(eventSection: string, eventMethod: string): Promise<EventCapture> {884 const capture = new EventCapture(this.helper, eventSection, eventMethod);885 await capture.startCapture();886887 return capture;888 }889890 makeXcmProgramWithdrawDeposit(beneficiary: Uint8Array, id: any, amount: bigint) {891 return {892 V2: [893 {894 WithdrawAsset: [895 {896 id,897 fun: {898 Fungible: amount,899 },900 },901 ],902 },903 {904 BuyExecution: {905 fees: {906 id,907 fun: {908 Fungible: amount,909 },910 },911 weightLimit: 'Unlimited',912 },913 },914 {915 DepositAsset: {916 assets: {917 Wild: 'All',918 },919 maxAssets: 1,920 beneficiary: {921 parents: 0,922 interior: {923 X1: {924 AccountId32: {925 network: 'Any',926 id: beneficiary,927 },928 },929 },930 },931 },932 },933 ],934 };935 }936937 makeXcmProgramReserveAssetDeposited(beneficiary: Uint8Array, id: any, amount: bigint) {938 return {939 V2: [940 {941 ReserveAssetDeposited: [942 {943 id,944 fun: {945 Fungible: amount,946 },947 },948 ],949 },950 {951 BuyExecution: {952 fees: {953 id,954 fun: {955 Fungible: amount,956 },957 },958 weightLimit: 'Unlimited',959 },960 },961 {962 DepositAsset: {963 assets: {964 Wild: 'All',965 },966 maxAssets: 1,967 beneficiary: {968 parents: 0,969 interior: {970 X1: {971 AccountId32: {972 network: 'Any',973 id: beneficiary,974 },975 },976 },977 },978 },979 },980 ],981 };982 }983984 makeUnpaidSudoTransactProgram(info: {weightMultiplier: number, call: string}) {985 return {986 V3: [987 {988 UnpaidExecution: {989 weightLimit: 'Unlimited',990 checkOrigin: null,991 },992 },993 {994 Transact: {995 originKind: 'Superuser',996 requireWeightAtMost: {997 refTime: info.weightMultiplier * 200000000,998 proofSize: info.weightMultiplier * 3000,999 },1000 call: {1001 encoded: info.call,1002 },1003 },1004 },1005 ],1006 };1007 }1008}10091010class MoonbeamAccountGroup {1011 helper: MoonbeamHelper;10121013 keyring: Keyring;1014 _alithAccount: IKeyringPair;1015 _baltatharAccount: IKeyringPair;1016 _dorothyAccount: IKeyringPair;10171018 constructor(helper: MoonbeamHelper) {1019 this.helper = helper;10201021 this.keyring = new Keyring({type: 'ethereum'});1022 const alithPrivateKey = '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133';1023 const baltatharPrivateKey = '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b';1024 const dorothyPrivateKey = '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68';10251026 this._alithAccount = this.keyring.addFromUri(alithPrivateKey, undefined, 'ethereum');1027 this._baltatharAccount = this.keyring.addFromUri(baltatharPrivateKey, undefined, 'ethereum');1028 this._dorothyAccount = this.keyring.addFromUri(dorothyPrivateKey, undefined, 'ethereum');1029 }10301031 alithAccount() {1032 return this._alithAccount;1033 }10341035 baltatharAccount() {1036 return this._baltatharAccount;1037 }10381039 dorothyAccount() {1040 return this._dorothyAccount;1041 }10421043 create() {1044 return this.keyring.addFromUri(mnemonicGenerate());1045 }1046}10471048class MoonbeamFastDemocracyGroup {1049 helper: DevMoonbeamHelper;10501051 constructor(helper: DevMoonbeamHelper) {1052 this.helper = helper;1053 }10541055 async executeProposal(proposalDesciption: string, encodedProposal: string) {1056 const proposalHash = blake2AsHex(encodedProposal);10571058 const alithAccount = this.helper.account.alithAccount();1059 const baltatharAccount = this.helper.account.baltatharAccount();1060 const dorothyAccount = this.helper.account.dorothyAccount();10611062 const councilVotingThreshold = 2;1063 const technicalCommitteeThreshold = 2;1064 const fastTrackVotingPeriod = 3;1065 const fastTrackDelayPeriod = 0;10661067 console.log(`[democracy] executing '${proposalDesciption}' proposal`);10681069 1070 console.log('\t* Propose external motion through council.......');1071 const externalMotion = this.helper.democracy.externalProposeMajority({Inline: encodedProposal});1072 const encodedMotion = externalMotion?.method.toHex() || '';1073 const motionHash = blake2AsHex(encodedMotion);1074 console.log('\t* Motion hash is %s', motionHash);10751076 await this.helper.collective.council.propose(1077 baltatharAccount,1078 councilVotingThreshold,1079 externalMotion,1080 externalMotion.encodedLength,1081 );10821083 const councilProposalIdx = await this.helper.collective.council.proposalCount() - 1;1084 await this.helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);1085 await this.helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);10861087 await this.helper.collective.council.close(1088 dorothyAccount,1089 motionHash,1090 councilProposalIdx,1091 {1092 refTime: 1_000_000_000,1093 proofSize: 1_000_000,1094 },1095 externalMotion.encodedLength,1096 );1097 console.log('\t* Propose external motion through council.......DONE');1098 10991100 1101 console.log('\t* Fast track proposal through technical committee.......');1102 const fastTrack = this.helper.democracy.fastTrack(proposalHash, fastTrackVotingPeriod, fastTrackDelayPeriod);1103 const encodedFastTrack = fastTrack?.method.toHex() || '';1104 const fastTrackHash = blake2AsHex(encodedFastTrack);1105 console.log('\t* FastTrack hash is %s', fastTrackHash);11061107 await this.helper.collective.techCommittee.propose(alithAccount, technicalCommitteeThreshold, fastTrack, fastTrack.encodedLength);11081109 const techProposalIdx = await this.helper.collective.techCommittee.proposalCount() - 1;1110 await this.helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);1111 await this.helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);11121113 await this.helper.collective.techCommittee.close(1114 baltatharAccount,1115 fastTrackHash,1116 techProposalIdx,1117 {1118 refTime: 1_000_000_000,1119 proofSize: 1_000_000,1120 },1121 fastTrack.encodedLength,1122 );1123 console.log('\t* Fast track proposal through technical committee.......DONE');1124 11251126 const democracyStarted = await this.helper.wait.expectEvent(3, Event.Democracy.Started);1127 const referendumIndex = democracyStarted.referendumIndex;11281129 1130 console.log(`\t* Referendum #${referendumIndex} voting.......`);1131 await this.helper.democracy.referendumVote(dorothyAccount, referendumIndex, {1132 balance: 10_000_000_000_000_000_000n,1133 vote: {aye: true, conviction: 1},1134 });1135 console.log(`\t* Referendum #${referendumIndex} voting.......DONE`);1136 11371138 1139 await this.helper.wait.expectEvent(3, Event.Democracy.Passed, event => event.referendumIndex == referendumIndex);11401141 await this.helper.wait.newBlocks(1);11421143 console.log(`[democracy] executing '${proposalDesciption}' proposal.......DONE`);1144 }1145}11461147class WaitGroup {1148 helper: ChainHelperBase;11491150 constructor(helper: ChainHelperBase) {1151 this.helper = helper;1152 }11531154 sleep(milliseconds: number) {1155 return new Promise((resolve) => setTimeout(resolve, milliseconds));1156 }11571158 private async waitWithTimeout(promise: Promise<any>, timeout: number) {1159 let isBlock = false;1160 promise.then(() => isBlock = true).catch(() => isBlock = true);1161 let totalTime = 0;1162 const step = 100;1163 while(!isBlock) {1164 await this.sleep(step);1165 totalTime += step;1166 if(totalTime >= timeout) throw Error('Blocks production failed');1167 }1168 return promise;1169 }11701171 1172117311741175117611771178 withTimeout<T>(1179 promise: Promise<T>,1180 timeoutMS = 30000,1181 timeoutError = 'The operation has timed out!',1182 ): Promise<T> {1183 const timeout = new Promise<never>((_, reject) => {1184 setTimeout(() => {1185 reject(new Error(timeoutError));1186 }, timeoutMS);1187 });11881189 return Promise.race<T>([promise, timeout]).catch(e => {throw new Error(e);});1190 }11911192 11931194119511961197 async newBlocks(blocksCount = 1, timeout?: number): Promise<void> {1198 timeout = timeout ?? blocksCount * 60_000;1199 1200 const promise = new Promise<void>(async (resolve) => {1201 const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads(() => {1202 if(blocksCount > 0) {1203 blocksCount--;1204 } else {1205 unsubscribe();1206 resolve();1207 }1208 });1209 });1210 await this.waitWithTimeout(promise, timeout);1211 return promise;1212 }12131214 1215121612171218121912201221 async newSessions(sessionCount = 1, blockTimeout = 60000): Promise<void> {1222 console.log(`Waiting for ${sessionCount} new session${sessionCount > 1 ? 's' : ''}.`1223 + ' This might take a while -- check SessionPeriod in pallet_session::Config for session time.');12241225 const expectedSessionIndex = await (this.helper as DevUniqueHelper).session.getIndex() + sessionCount;1226 let currentSessionIndex = -1;12271228 while(currentSessionIndex < expectedSessionIndex) {1229 1230 currentSessionIndex = await this.withTimeout(new Promise(async (resolve) => {1231 await this.newBlocks(1);1232 const res = await (this.helper as DevUniqueHelper).session.getIndex();1233 resolve(res);1234 }), blockTimeout, 'The chain has stopped producing blocks!');1235 }1236 }12371238 async forParachainBlockNumber(blockNumber: bigint | number, timeout?: number) {1239 timeout = timeout ?? 30 * 60 * 1000;1240 1241 const promise = new Promise<void>(async (resolve) => {1242 const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads((data: any) => {1243 if(data.number.toNumber() >= blockNumber) {1244 unsubscribe();1245 resolve();1246 }1247 });1248 });1249 await this.waitWithTimeout(promise, timeout);1250 return promise;1251 }12521253 async forRelayBlockNumber(blockNumber: bigint | number, timeout?: number) {1254 timeout = timeout ?? 30 * 60 * 1000;1255 1256 const promise = new Promise<void>(async (resolve) => {1257 const unsubscribe = await this.helper.getApi().query.parachainSystem.validationData((data: any) => {1258 if(data.value.relayParentNumber.toNumber() >= blockNumber) {1259 1260 unsubscribe();1261 resolve();1262 }1263 });1264 });1265 await this.waitWithTimeout(promise, timeout);1266 return promise;1267 }12681269 noScheduledTasks() {1270 const api = this.helper.getApi();12711272 1273 const promise = new Promise<void>(async resolve => {1274 const unsubscribe = await api.rpc.chain.subscribeNewHeads(async () => {1275 const areThereScheduledTasks = await api.query.scheduler.lookup.entries();12761277 if(areThereScheduledTasks.length == 0) {1278 unsubscribe();1279 resolve();1280 }1281 });1282 });12831284 return promise;1285 }12861287 parachainBlockMultiplesOf(val: bigint) {1288 1289 const promise = new Promise<void>(async resolve => {1290 const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads((data: any) => {1291 if(data.number.toBigInt() % val == 0n) {1292 console.log(`from waiter: ${data.number.toBigInt()}`);1293 unsubscribe();1294 resolve();1295 }1296 });1297 });1298 return promise;1299 }13001301 event<T extends IEventHelper>(1302 maxBlocksToWait: number,1303 eventHelper: T,1304 filter: (_: any) => boolean = () => true,1305 ): any {1306 1307 const promise = new Promise<T | null>(async (resolve) => {1308 const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads(async header => {1309 const blockNumber = header.number.toJSON();1310 const blockHash = header.hash;1311 const eventIdStr = `${eventHelper.section()}.${eventHelper.method()}`;1312 const waitLimitStr = `wait blocks remaining: ${maxBlocksToWait}`;13131314 this.helper.logger.log(`[Block #${blockNumber}] Waiting for event \`${eventIdStr}\` (${waitLimitStr})`);13151316 const apiAt = await this.helper.getApi().at(blockHash);1317 const eventRecords = (await apiAt.query.system.events()) as any;13181319 const neededEvent = eventRecords.toArray()1320 .filter((r: FrameSystemEventRecord) => r.event.section == eventHelper.section() && r.event.method == eventHelper.method())1321 .map((r: FrameSystemEventRecord) => eventHelper.wrapEvent(r.event.data))1322 .find(filter);13231324 if(neededEvent) {1325 unsubscribe();1326 resolve(neededEvent);1327 } else if(maxBlocksToWait > 0) {1328 maxBlocksToWait--;1329 } else {1330 this.helper.logger.log(`Eligible event \`${eventIdStr}\` is NOT found.1331 The wait lasted until block ${blockNumber} inclusive`);1332 unsubscribe();1333 resolve(null);1334 }1335 });1336 });1337 return promise;1338 }13391340 async expectEvent<T extends IEventHelper>(1341 maxBlocksToWait: number,1342 eventHelper: T,1343 filter: (e: any) => boolean = () => true,1344 ) {1345 const e = await this.event(maxBlocksToWait, eventHelper, filter);1346 if(e == null) {1347 throw Error(`The event '${eventHelper.section()}.${eventHelper.method()}' is expected`);1348 } else {1349 return e;1350 }1351 }1352}13531354class SessionGroup {1355 helper: ChainHelperBase;13561357 constructor(helper: ChainHelperBase) {1358 this.helper = helper;1359 }13601361 1362 async getIndex(): Promise<number> {1363 return (await this.helper.callRpc('api.query.session.currentIndex', [])).toNumber();1364 }13651366 newSessions(sessionCount = 1, blockTimeout = 24000): Promise<void> {1367 return (this.helper as DevUniqueHelper).wait.newSessions(sessionCount, blockTimeout);1368 }13691370 setOwnKeys(signer: TSigner, key: string) {1371 return this.helper.executeExtrinsic(1372 signer,1373 'api.tx.session.setKeys',1374 [key, '0x0'],1375 true,1376 );1377 }13781379 setOwnKeysFromAddress(signer: TSigner) {1380 return this.setOwnKeys(signer, '0x' + Buffer.from(signer.addressRaw).toString('hex'));1381 }1382}13831384class TestUtilGroup {1385 helper: DevUniqueHelper;13861387 constructor(helper: DevUniqueHelper) {1388 this.helper = helper;1389 }13901391 async enable() {1392 if(this.helper.fetchMissingPalletNames([Pallets.TestUtils]).length != 0) {1393 return;1394 }13951396 const signer = this.helper.util.fromSeed('//Alice');1397 await this.helper.getSudo<DevUniqueHelper>().executeExtrinsic(signer, 'api.tx.testUtils.enable', [], true);1398 }13991400 async setTestValue(signer: TSigner, testVal: number) {1401 await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.setTestValue', [testVal], true);1402 }14031404 async incTestValue(signer: TSigner) {1405 await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.incTestValue', [], true);1406 }14071408 async setTestValueAndRollback(signer: TSigner, testVal: number) {1409 await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.setTestValueAndRollback', [testVal], true);1410 }14111412 async testValue(blockIdx?: number) {1413 const api = blockIdx1414 ? await this.helper.getApi().at(await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockIdx]))1415 : this.helper.getApi();14161417 return (await api.query.testUtils.testValue()).toJSON();1418 }14191420 async justTakeFee(signer: TSigner) {1421 await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.justTakeFee', [], true);1422 }14231424 async selfCancelingInc(signer: TSigner, scheduledId: string, maxTestVal: number) {1425 await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.selfCancelingInc', [scheduledId, maxTestVal], true);1426 }1427}14281429class EventCapture {1430 helper: DevUniqueHelper;1431 eventSection: string;1432 eventMethod: string;1433 events: EventRecord[] = [];1434 unsubscribe: VoidFn | null = null;14351436 constructor(1437 helper: DevUniqueHelper,1438 eventSection: string,1439 eventMethod: string,1440 ) {1441 this.helper = helper;1442 this.eventSection = eventSection;1443 this.eventMethod = eventMethod;1444 }14451446 async startCapture() {1447 this.stopCapture();1448 this.unsubscribe = (await this.helper.getApi().query.system.events((eventRecords: FrameSystemEventRecord[]) => {1449 const newEvents = eventRecords.filter(r => r.event.section == this.eventSection && r.event.method == this.eventMethod);14501451 this.events.push(...newEvents);1452 })) as any;1453 }14541455 stopCapture() {1456 if(this.unsubscribe !== null) {1457 this.unsubscribe();1458 }1459 }14601461 extractCapturedEvents() {1462 return this.events;1463 }1464}14651466class AdminGroup {1467 helper: UniqueHelper;14681469 constructor(helper: UniqueHelper) {1470 this.helper = helper;1471 }14721473 async payoutStakers(signer: IKeyringPair, stakersToPayout: number): Promise<{staker: string, stake: bigint, payout: bigint}[]> {1474 const payoutResult = await this.helper.executeExtrinsic(signer, 'api.tx.appPromotion.payoutStakers', [stakersToPayout], true);1475 return payoutResult.result.events.filter(e => e.event.method === 'StakingRecalculation').map(e => ({1476 staker: e.event.data[0].toString(),1477 stake: e.event.data[1].toBigInt(),1478 payout: e.event.data[2].toBigInt(),1479 }));1480 }1481}148214831484function ScheduledUniqueHelper<T extends UniqueHelperConstructor>(Base: T) {1485 return class extends Base {1486 scheduleFn: 'schedule' | 'scheduleAfter';1487 blocksNum: number;1488 options: ISchedulerOptions;14891490 constructor(...args: any[]) {1491 const logger = args[0] as ILogger;1492 const options = args[1] as {1493 scheduleFn: 'schedule' | 'scheduleAfter',1494 blocksNum: number,1495 options: ISchedulerOptions1496 };14971498 super(logger);14991500 this.scheduleFn = options.scheduleFn;1501 this.blocksNum = options.blocksNum;1502 this.options = options.options;1503 }15041505 executeExtrinsic(sender: IKeyringPair, scheduledExtrinsic: string, scheduledParams: any[], expectSuccess?: boolean): Promise<ITransactionResult> {1506 const scheduledTx = this.constructApiCall(scheduledExtrinsic, scheduledParams);15071508 const mandatorySchedArgs = [1509 this.blocksNum,1510 this.options.periodic ? [this.options.periodic.period, this.options.periodic.repetitions] : null,1511 this.options.priority ?? null,1512 scheduledTx,1513 ];15141515 let schedArgs;1516 let scheduleFn;15171518 if(this.options.scheduledId) {1519 schedArgs = [this.options.scheduledId!, ...mandatorySchedArgs];15201521 if(this.scheduleFn == 'schedule') {1522 scheduleFn = 'scheduleNamed';1523 } else if(this.scheduleFn == 'scheduleAfter') {1524 scheduleFn = 'scheduleNamedAfter';1525 }1526 } else {1527 schedArgs = mandatorySchedArgs;1528 scheduleFn = this.scheduleFn;1529 }15301531 const extrinsic = 'api.tx.scheduler.' + scheduleFn;15321533 return super.executeExtrinsic(1534 sender,1535 extrinsic as any,1536 schedArgs,1537 expectSuccess,1538 );1539 }1540 };1541}