git.delta.rocks / unique-network / refs/commits / 04e6d946dd25

difftreelog

tests: move scheduler to dev, move governance to separate file

Andy Smith2023-09-14parent: #383d45a.patch.diff
in: master

7 files changed

modifiedtests/src/governance/util.tsdiffbeforeafterboth
2import {xxhashAsHex} from '@polkadot/util-crypto';2import {xxhashAsHex} from '@polkadot/util-crypto';
3import {usingPlaygrounds, expect} from '../util';3import {usingPlaygrounds, expect} from '../util';
4import {UniqueHelper} from '../util/playgrounds/unique';4import {UniqueHelper} from '../util/playgrounds/unique';
5import {DevUniqueHelper} from '../util/playgrounds/unique.dev';
56
6export const democracyLaunchPeriod = 35;7export const democracyLaunchPeriod = 35;
7export const democracyVotingPeriod = 35;8export const democracyVotingPeriod = 35;
203 });204 });
204}205}
205206
206export async function voteUnanimouslyInFellowship(helper: UniqueHelper, fellows: IKeyringPair[][], minRank: number, referendumIndex: number) {207export async function voteUnanimouslyInFellowship(helper: DevUniqueHelper, fellows: IKeyringPair[][], minRank: number, referendumIndex: number) {
207 for(let rank = minRank; rank < fellowshipRankLimit; rank++) {208 for(let rank = minRank; rank < fellowshipRankLimit; rank++) {
208 for(const member of fellows[rank]) {209 for(const member of fellows[rank]) {
209 await helper.fellowship.collective.vote(member, referendumIndex, true);210 await helper.fellowship.collective.vote(member, referendumIndex, true);
modifiedtests/src/util/playgrounds/types.tsdiffbeforeafterboth
216 },216 },
217}217}
218
219export interface IForeignAssetMetadata {
220 name?: number | Uint8Array,
221 symbol?: string,
222 decimals?: number,
223 minimalBalance?: bigint,
224}
225218
226export interface DemocracySplitAccount {219export interface DemocracySplitAccount {
227 aye: bigint,220 aye: bigint,
modifiedtests/src/util/playgrounds/types.xcm.tsdiffbeforeafterboth
28 },28 },
29}29}
30
31export interface IForeignAssetMetadata {
32 name?: number | Uint8Array,
33 symbol?: string,
34 decimals?: number,
35 minimalBalance?: bigint,
36}
modifiedtests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth
33
4import {stringToU8a} from '@polkadot/util';4import {stringToU8a} from '@polkadot/util';
5import {blake2AsHex, encodeAddress, mnemonicGenerate} from '@polkadot/util-crypto';5import {blake2AsHex, encodeAddress, mnemonicGenerate} from '@polkadot/util-crypto';
6import {UniqueHelper, ChainHelperBase, ChainHelperBaseConstructor} from './unique';6import {UniqueHelper, ChainHelperBase, ChainHelperBaseConstructor, HelperGroup, UniqueHelperConstructor} from './unique';
7import {ApiPromise, Keyring, WsProvider} from '@polkadot/api';7import {ApiPromise, Keyring, WsProvider} from '@polkadot/api';
8import * as defs from '../../interfaces/definitions';8import * as defs from '../../interfaces/definitions';
9import {IKeyringPair} from '@polkadot/types/types';9import {IKeyringPair} from '@polkadot/types/types';
10import {EventRecord} from '@polkadot/types/interfaces';10import {EventRecord} from '@polkadot/types/interfaces';
11import {ICrossAccountId, IPovInfo, ITransactionResult, TSigner} from './types';11import {ICrossAccountId, ILogger, IPovInfo, ISchedulerOptions, ITransactionResult, TSigner} from './types';
12import {FrameSystemEventRecord, XcmV2TraitsError} from '@polkadot/types/lookup';12import {FrameSystemEventRecord, XcmV2TraitsError} from '@polkadot/types/lookup';
13import {SignerOptions, VoidFn} from '@polkadot/api/types';13import {SignerOptions, VoidFn} from '@polkadot/api/types';
14import {Pallets} from '..';14import {Pallets} from '..';
15import {spawnSync} from 'child_process';15import {spawnSync} from 'child_process';
16import {AcalaHelper, AstarHelper, MoonbeamHelper, PolkadexHelper, RelayHelper, WestmintHelper} from './unique.xcm';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';
1718
18export class SilentLogger {19export class SilentLogger {
19 log(_msg: any, _level: any): void { }20 log(_msg: any, _level: any): void { }
335 };336 };
336}337}
338
339class SchedulerGroup extends HelperGroup<UniqueHelper> {
340 constructor(helper: UniqueHelper) {
341 super(helper);
342 }
343
344 cancelScheduled(signer: TSigner, scheduledId: string) {
345 return this.helper.executeExtrinsic(
346 signer,
347 'api.tx.scheduler.cancelNamed',
348 [scheduledId],
349 true,
350 );
351 }
352
353 changePriority(signer: TSigner, scheduledId: string, priority: number) {
354 return this.helper.executeExtrinsic(
355 signer,
356 'api.tx.scheduler.changeNamedPriority',
357 [scheduledId, priority],
358 true,
359 );
360 }
361
362 scheduleAt<T extends DevUniqueHelper>(
363 executionBlockNumber: number,
364 options: ISchedulerOptions = {},
365 ) {
366 return this.schedule<T>('schedule', executionBlockNumber, options);
367 }
368
369 scheduleAfter<T extends DevUniqueHelper>(
370 blocksBeforeExecution: number,
371 options: ISchedulerOptions = {},
372 ) {
373 return this.schedule<T>('scheduleAfter', blocksBeforeExecution, options);
374 }
375
376 schedule<T extends UniqueHelper>(
377 scheduleFn: 'schedule' | 'scheduleAfter',
378 blocksNum: number,
379 options: ISchedulerOptions = {},
380 ) {
381 // eslint-disable-next-line @typescript-eslint/naming-convention
382 const ScheduledHelperType = ScheduledUniqueHelper(this.helper.helperBase);
383 return this.helper.clone(ScheduledHelperType, {
384 scheduleFn,
385 blocksNum,
386 options,
387 }) as T;
388 }
389}
390
391class CollatorSelectionGroup extends HelperGroup<UniqueHelper> {
392 //todo:collator documentation
393 addInvulnerable(signer: TSigner, address: string) {
394 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.addInvulnerable', [address]);
395 }
396
397 removeInvulnerable(signer: TSigner, address: string) {
398 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.removeInvulnerable', [address]);
399 }
400
401 async getInvulnerables(): Promise<string[]> {
402 return (await this.helper.callRpc('api.query.collatorSelection.invulnerables')).map((x: any) => x.toHuman());
403 }
404
405 /** and also total max invulnerables */
406 maxCollators(): number {
407 return (this.helper.getApi().consts.configuration.defaultCollatorSelectionMaxCollators.toJSON() as number);
408 }
409
410 async getDesiredCollators(): Promise<number> {
411 return (await this.helper.callRpc('api.query.configuration.collatorSelectionDesiredCollatorsOverride')).toNumber();
412 }
413
414 setLicenseBond(signer: TSigner, amount: bigint) {
415 return this.helper.executeExtrinsic(signer, 'api.tx.configuration.setCollatorSelectionLicenseBond', [amount]);
416 }
417
418 async getLicenseBond(): Promise<bigint> {
419 return (await this.helper.callRpc('api.query.configuration.collatorSelectionLicenseBondOverride')).toBigInt();
420 }
421
422 obtainLicense(signer: TSigner) {
423 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.getLicense', []);
424 }
425
426 releaseLicense(signer: TSigner) {
427 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.releaseLicense', []);
428 }
429
430 forceReleaseLicense(signer: TSigner, released: string) {
431 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.forceReleaseLicense', [released]);
432 }
433
434 async hasLicense(address: string): Promise<bigint> {
435 return (await this.helper.callRpc('api.query.collatorSelection.licenseDepositOf', [address])).toBigInt();
436 }
437
438 onboard(signer: TSigner) {
439 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.onboard', []);
440 }
441
442 offboard(signer: TSigner) {
443 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.offboard', []);
444 }
445
446 async getCandidates(): Promise<string[]> {
447 return (await this.helper.callRpc('api.query.collatorSelection.candidates')).map((x: any) => x.toHuman());
448 }
449}
337450
338export class DevUniqueHelper extends UniqueHelper {451export class DevUniqueHelper extends UniqueHelper {
339 /**452 /**
344 admin: AdminGroup;457 admin: AdminGroup;
345 session: SessionGroup;458 session: SessionGroup;
346 testUtils: TestUtilGroup;459 testUtils: TestUtilGroup;
460 foreignAssets: ForeignAssetsGroup;
461 xcm: XcmGroup<UniqueHelper>;
462 xTokens: XTokensGroup<UniqueHelper>;
463 tokens: TokensGroup<UniqueHelper>;
464 scheduler: SchedulerGroup;
465 collatorSelection: CollatorSelectionGroup;
466 council: ICollectiveGroup;
467 technicalCommittee: ICollectiveGroup;
468 fellowship: IFellowshipGroup;
469 democracy: DemocracyGroup;
347470
348 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {471 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
349 options.helperBase = options.helperBase ?? DevUniqueHelper;472 options.helperBase = options.helperBase ?? DevUniqueHelper;
354 this.admin = new AdminGroup(this);477 this.admin = new AdminGroup(this);
355 this.testUtils = new TestUtilGroup(this);478 this.testUtils = new TestUtilGroup(this);
356 this.session = new SessionGroup(this);479 this.session = new SessionGroup(this);
480 this.foreignAssets = new ForeignAssetsGroup(this);
481 this.xcm = new XcmGroup(this, 'polkadotXcm');
482 this.xTokens = new XTokensGroup(this);
483 this.tokens = new TokensGroup(this);
484 this.scheduler = new SchedulerGroup(this);
485 this.collatorSelection = new CollatorSelectionGroup(this);
486 this.council = {
487 collective: new CollectiveGroup(this, 'council'),
488 membership: new CollectiveMembershipGroup(this, 'councilMembership'),
489 };
490 this.technicalCommittee = {
491 collective: new CollectiveGroup(this, 'technicalCommittee'),
492 membership: new CollectiveMembershipGroup(this, 'technicalCommitteeMembership'),
493 };
494 this.fellowship = {
495 collective: new RankedCollectiveGroup(this, 'fellowshipCollective'),
496 referenda: new ReferendaGroup(this, 'fellowshipReferenda'),
497 };
498 this.democracy = new DemocracyGroup(this);
357 }499 }
358500
359 async connect(wsEndpoint: string, _listeners?: any): Promise<void> {501 async connect(wsEndpoint: string, _listeners?: any): Promise<void> {
401 this.network = await UniqueHelper.detectNetwork(this.api);543 this.network = await UniqueHelper.detectNetwork(this.api);
402 this.wsEndpoint = wsEndpoint;544 this.wsEndpoint = wsEndpoint;
403 }545 }
404 getSudo<T extends UniqueHelper>() {546 getSudo<T extends DevUniqueHelper>() {
405 // eslint-disable-next-line @typescript-eslint/naming-convention547 // eslint-disable-next-line @typescript-eslint/naming-convention
406 const SudoHelperType = SudoHelper(this.helperBase);548 const SudoHelperType = SudoHelper(this.helperBase);
407 return this.clone(SudoHelperType) as T;549 return this.clone(SudoHelperType) as T;
1309 }1451 }
1310}1452}
13111453
1454// eslint-disable-next-line @typescript-eslint/naming-convention
1455function ScheduledUniqueHelper<T extends UniqueHelperConstructor>(Base: T) {
1456 return class extends Base {
1457 scheduleFn: 'schedule' | 'scheduleAfter';
1458 blocksNum: number;
1459 options: ISchedulerOptions;
1460
1461 constructor(...args: any[]) {
1462 const logger = args[0] as ILogger;
1463 const options = args[1] as {
1464 scheduleFn: 'schedule' | 'scheduleAfter',
1465 blocksNum: number,
1466 options: ISchedulerOptions
1467 };
1468
1469 super(logger);
1470
1471 this.scheduleFn = options.scheduleFn;
1472 this.blocksNum = options.blocksNum;
1473 this.options = options.options;
1474 }
1475
1476 executeExtrinsic(sender: IKeyringPair, scheduledExtrinsic: string, scheduledParams: any[], expectSuccess?: boolean): Promise<ITransactionResult> {
1477 const scheduledTx = this.constructApiCall(scheduledExtrinsic, scheduledParams);
1478
1479 const mandatorySchedArgs = [
1480 this.blocksNum,
1481 this.options.periodic ? [this.options.periodic.period, this.options.periodic.repetitions] : null,
1482 this.options.priority ?? null,
1483 scheduledTx,
1484 ];
1485
1486 let schedArgs;
1487 let scheduleFn;
1488
1489 if(this.options.scheduledId) {
1490 schedArgs = [this.options.scheduledId!, ...mandatorySchedArgs];
1491
1492 if(this.scheduleFn == 'schedule') {
1493 scheduleFn = 'scheduleNamed';
1494 } else if(this.scheduleFn == 'scheduleAfter') {
1495 scheduleFn = 'scheduleNamedAfter';
1496 }
1497 } else {
1498 schedArgs = mandatorySchedArgs;
1499 scheduleFn = this.scheduleFn;
1500 }
1501
1502 const extrinsic = 'api.tx.scheduler.' + scheduleFn;
1503
1504 return super.executeExtrinsic(
1505 sender,
1506 extrinsic as any,
1507 schedArgs,
1508 expectSuccess,
1509 );
1510 }
1511 };
1512}
addedtests/src/util/playgrounds/unique.governance.tsdiffbeforeafterboth

no changes

modifiedtests/src/util/playgrounds/unique.tsdiffbeforeafterboth
39 TSigner,39 TSigner,
40 TSubstrateAccount,40 TSubstrateAccount,
41 TNetworks,41 TNetworks,
42 IForeignAssetMetadata,
43 IEthCrossAccountId,42 IEthCrossAccountId,
44 IPhasicEvent,
45} from './types';43} from './types';
46import {RuntimeDispatchInfo} from '@polkadot/types/interfaces';44import {RuntimeDispatchInfo} from '@polkadot/types/interfaces';
47import type {Vec} from '@polkadot/types-codec';45import type {Vec} from '@polkadot/types-codec';
48import {FrameSystemEventRecord, PalletDemocracyConviction} from '@polkadot/types/lookup';46import {FrameSystemEventRecord} from '@polkadot/types/lookup';
4947
50export class CrossAccountId {48export class CrossAccountId {
51 Substrate!: TSubstrateAccount;49 Substrate!: TSubstrateAccount;
2864 }2863 }
2865}2864}
28662865
2867class SchedulerGroup extends HelperGroup<UniqueHelper> {
2868 constructor(helper: UniqueHelper) {
2869 super(helper);
2870 }
2871
2872 cancelScheduled(signer: TSigner, scheduledId: string) {
2873 return this.helper.executeExtrinsic(
2874 signer,
2875 'api.tx.scheduler.cancelNamed',
2876 [scheduledId],
2877 true,
2878 );
2879 }
2880
2881 changePriority(signer: TSigner, scheduledId: string, priority: number) {
2882 return this.helper.executeExtrinsic(
2883 signer,
2884 'api.tx.scheduler.changeNamedPriority',
2885 [scheduledId, priority],
2886 true,
2887 );
2888 }
2889
2890 scheduleAt<T extends UniqueHelper>(
2891 executionBlockNumber: number,
2892 options: ISchedulerOptions = {},
2893 ) {
2894 return this.schedule<T>('schedule', executionBlockNumber, options);
2895 }
2896
2897 scheduleAfter<T extends UniqueHelper>(
2898 blocksBeforeExecution: number,
2899 options: ISchedulerOptions = {},
2900 ) {
2901 return this.schedule<T>('scheduleAfter', blocksBeforeExecution, options);
2902 }
2903
2904 schedule<T extends UniqueHelper>(
2905 scheduleFn: 'schedule' | 'scheduleAfter',
2906 blocksNum: number,
2907 options: ISchedulerOptions = {},
2908 ) {
2909 // eslint-disable-next-line @typescript-eslint/naming-convention
2910 const ScheduledHelperType = ScheduledUniqueHelper(this.helper.helperBase);
2911 return this.helper.clone(ScheduledHelperType, {
2912 scheduleFn,
2913 blocksNum,
2914 options,
2915 }) as T;
2916 }
2917}
2918
2919class CollatorSelectionGroup extends HelperGroup<UniqueHelper> {
2920 //todo:collator documentation
2921 addInvulnerable(signer: TSigner, address: string) {
2922 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.addInvulnerable', [address]);
2923 }
2924
2925 removeInvulnerable(signer: TSigner, address: string) {
2926 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.removeInvulnerable', [address]);
2927 }
2928
2929 async getInvulnerables(): Promise<string[]> {
2930 return (await this.helper.callRpc('api.query.collatorSelection.invulnerables')).map((x: any) => x.toHuman());
2931 }
2932
2933 /** and also total max invulnerables */
2934 maxCollators(): number {
2935 return (this.helper.getApi().consts.configuration.defaultCollatorSelectionMaxCollators.toJSON() as number);
2936 }
2937
2938 async getDesiredCollators(): Promise<number> {
2939 return (await this.helper.callRpc('api.query.configuration.collatorSelectionDesiredCollatorsOverride')).toNumber();
2940 }
2941
2942 setLicenseBond(signer: TSigner, amount: bigint) {
2943 return this.helper.executeExtrinsic(signer, 'api.tx.configuration.setCollatorSelectionLicenseBond', [amount]);
2944 }
2945
2946 async getLicenseBond(): Promise<bigint> {
2947 return (await this.helper.callRpc('api.query.configuration.collatorSelectionLicenseBondOverride')).toBigInt();
2948 }
2949
2950 obtainLicense(signer: TSigner) {
2951 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.getLicense', []);
2952 }
2953
2954 releaseLicense(signer: TSigner) {
2955 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.releaseLicense', []);
2956 }
2957
2958 forceReleaseLicense(signer: TSigner, released: string) {
2959 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.forceReleaseLicense', [released]);
2960 }
2961
2962 async hasLicense(address: string): Promise<bigint> {
2963 return (await this.helper.callRpc('api.query.collatorSelection.licenseDepositOf', [address])).toBigInt();
2964 }
2965
2966 onboard(signer: TSigner) {
2967 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.onboard', []);
2968 }
2969
2970 offboard(signer: TSigner) {
2971 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.offboard', []);
2972 }
2973
2974 async getCandidates(): Promise<string[]> {
2975 return (await this.helper.callRpc('api.query.collatorSelection.candidates')).map((x: any) => x.toHuman());
2976 }
2977}
2978
2979class CollectiveGroup extends HelperGroup<UniqueHelper> {
2980 /**
2981 * Pallet name to make an API call to. Examples: 'council', 'technicalCommittee'
2982 */
2983 private collective: string;
2984
2985 constructor(helper: UniqueHelper, collective: string) {
2986 super(helper);
2987 this.collective = collective;
2988 }
2989
2990 /**
2991 * Check the result of a proposal execution for the success of the underlying proposed extrinsic.
2992 * @param events events of the proposal execution
2993 * @returns proposal hash
2994 */
2995 private checkExecutedEvent(events: IPhasicEvent[]): string {
2996 const executionEvents = events.filter(x =>
2997 x.event.section === this.collective && (x.event.method === 'Executed' || x.event.method === 'MemberExecuted'));
2998
2999 if(executionEvents.length != 1) {
3000 if(events.filter(x => x.event.section === this.collective && x.event.method === 'Disapproved').length > 0)
3001 throw new Error(`Disapproved by ${this.collective}`);
3002 else
3003 throw new Error(`Expected one 'Executed' or 'MemberExecuted' event for ${this.collective}`);
3004 }
3005
3006 const result = (executionEvents[0].event.data as any).result;
3007
3008 if(result.isErr) {
3009 if(result.asErr.isModule) {
3010 const error = result.asErr.asModule;
3011 const metaError = this.helper.getApi()?.registry.findMetaError(error);
3012 throw new Error(`Proposal execution failed with ${metaError.section}.${metaError.name}`);
3013 } else {
3014 throw new Error('Proposal execution failed with ' + result.asErr.toHuman());
3015 }
3016 }
3017
3018 return (executionEvents[0].event.data as any).proposalHash;
3019 }
3020
3021 /**
3022 * Returns an array of members' addresses.
3023 */
3024 async getMembers() {
3025 return (await this.helper.callRpc(`api.query.${this.collective}.members`, [])).toHuman();
3026 }
3027
3028 /**
3029 * Returns the optional address of the prime member of the collective.
3030 */
3031 async getPrimeMember() {
3032 return (await this.helper.callRpc(`api.query.${this.collective}.prime`, [])).toHuman();
3033 }
3034
3035 /**
3036 * Returns an array of proposal hashes that are currently active for this collective.
3037 */
3038 async getProposals() {
3039 return (await this.helper.callRpc(`api.query.${this.collective}.proposals`, [])).toHuman();
3040 }
3041
3042 /**
3043 * Returns the call originally encoded under the specified hash.
3044 * @param hash h256-encoded proposal
3045 * @returns the optional call that the proposal hash stands for.
3046 */
3047 async getProposalCallOf(hash: string) {
3048 return (await this.helper.callRpc(`api.query.${this.collective}.proposalOf`, [hash])).toHuman();
3049 }
3050
3051 /**
3052 * Returns the total number of proposals so far.
3053 */
3054 async getTotalProposalsCount() {
3055 return (await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, [])).toNumber();
3056 }
3057
3058 /**
3059 * Creates a new proposal up for voting. If the threshold is set to 1, the proposal will be executed immediately.
3060 * @param signer keyring of the proposer
3061 * @param proposal constructed call to be executed if the proposal is successful
3062 * @param voteThreshold minimal number of votes for the proposal to be verified and executed
3063 * @param lengthBound byte length of the encoded call
3064 * @returns promise of extrinsic execution and its result
3065 */
3066 async propose(signer: TSigner, proposal: any, voteThreshold: number, lengthBound = 10000) {
3067 return await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [voteThreshold, proposal, lengthBound]);
3068 }
3069
3070 /**
3071 * Casts a vote to either approve or reject a proposal.
3072 * @param signer keyring of the voter
3073 * @param proposalHash hash of the proposal to be voted for
3074 * @param proposalIndex absolute index of the proposal used for absolutely nothing but throwing pointless errors
3075 * @param approve aye or nay
3076 * @returns promise of extrinsic execution and its result
3077 */
3078 vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {
3079 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve]);
3080 }
3081
3082 /**
3083 * Executes a call immediately as a member of the collective. Needed for the Member origin.
3084 * @param signer keyring of the executor member
3085 * @param proposal constructed call to be executed by the member
3086 * @param lengthBound byte length of the encoded call
3087 * @returns promise of extrinsic execution
3088 */
3089 async execute(signer: TSigner, proposal: any, lengthBound = 10000) {
3090 const result = await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.execute`, [proposal, lengthBound]);
3091 this.checkExecutedEvent(result.result.events);
3092 return result;
3093 }
3094
3095 /**
3096 * Attempt to close and execute a proposal. Note that there must already be enough votes to meet the threshold set when proposing.
3097 * @param signer keyring of the executor. Can be absolutely anyone.
3098 * @param proposalHash hash of the proposal to close
3099 * @param proposalIndex index of the proposal generated on its creation
3100 * @param weightBound weight of the proposed call. Can be obtained by calling `paymentInfo()` on the call.
3101 * @param lengthBound byte length of the encoded call
3102 * @returns promise of extrinsic execution and its result
3103 */
3104 async close(
3105 signer: TSigner,
3106 proposalHash: string,
3107 proposalIndex: number,
3108 weightBound: [number, number] | any = [20_000_000_000, 1000_000],
3109 lengthBound = 10_000,
3110 ) {
3111 const result = await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [
3112 proposalHash,
3113 proposalIndex,
3114 weightBound,
3115 lengthBound,
3116 ]);
3117 this.checkExecutedEvent(result.result.events);
3118 return result;
3119 }
3120
3121 /**
3122 * Shut down a proposal, regardless of its current state.
3123 * @param signer keyring of the disapprover. Must be root
3124 * @param proposalHash hash of the proposal to close
3125 * @returns promise of extrinsic execution and its result
3126 */
3127 disapproveProposal(signer: TSigner, proposalHash: string) {
3128 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.disapproveProposal`, [proposalHash]);
3129 }
3130}
3131
3132class CollectiveMembershipGroup extends HelperGroup<UniqueHelper> {
3133 /**
3134 * Pallet name to make an API call to. Examples: 'councilMembership', 'technicalCommitteeMembership'
3135 */
3136 private membership: string;
3137
3138 constructor(helper: UniqueHelper, membership: string) {
3139 super(helper);
3140 this.membership = membership;
3141 }
3142
3143 /**
3144 * Returns an array of members' addresses according to the membership pallet's perception.
3145 * Note that it does not recognize the original pallet's members set with `setMembers()`.
3146 */
3147 async getMembers() {
3148 return (await this.helper.callRpc(`api.query.${this.membership}.members`, [])).toHuman();
3149 }
3150
3151 /**
3152 * Returns the optional address of the prime member of the collective.
3153 */
3154 async getPrimeMember() {
3155 return (await this.helper.callRpc(`api.query.${this.membership}.prime`, [])).toHuman();
3156 }
3157
3158 /**
3159 * Add a member to the collective.
3160 * @param signer keyring of the setter. Must be root
3161 * @param member address of the member to add
3162 * @returns promise of extrinsic execution and its result
3163 */
3164 addMember(signer: TSigner, member: string) {
3165 return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.addMember`, [member]);
3166 }
3167
3168 addMemberCall(member: string) {
3169 return this.helper.constructApiCall(`api.tx.${this.membership}.addMember`, [member]);
3170 }
3171
3172 /**
3173 * Remove a member from the collective.
3174 * @param signer keyring of the setter. Must be root
3175 * @param member address of the member to remove
3176 * @returns promise of extrinsic execution and its result
3177 */
3178 removeMember(signer: TSigner, member: string) {
3179 return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.removeMember`, [member]);
3180 }
3181
3182 removeMemberCall(member: string) {
3183 return this.helper.constructApiCall(`api.tx.${this.membership}.removeMember`, [member]);
3184 }
3185
3186 /**
3187 * Set members of the collective to the given list of addresses.
3188 * @param signer keyring of the setter. Must be root (for the direct call, bypassing a public motion)
3189 * @param members addresses of the members to set
3190 * @returns promise of extrinsic execution and its result
3191 */
3192 resetMembers(signer: TSigner, members: string[]) {
3193 return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.resetMembers`, [members]);
3194 }
3195
3196 /**
3197 * Set the collective's prime member to the given address.
3198 * @param signer keyring of the setter. Must be root (for the direct call, bypassing a public motion)
3199 * @param prime address of the prime member of the collective
3200 * @returns promise of extrinsic execution and its result
3201 */
3202 setPrime(signer: TSigner, prime: string) {
3203 return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.setPrime`, [prime]);
3204 }
3205
3206 setPrimeCall(member: string) {
3207 return this.helper.constructApiCall(`api.tx.${this.membership}.setPrime`, [member]);
3208 }
3209
3210 /**
3211 * Remove the collective's prime member.
3212 * @param signer keyring of the setter. Must be root (for the direct call, bypassing a public motion)
3213 * @returns promise of extrinsic execution and its result
3214 */
3215 clearPrime(signer: TSigner) {
3216 return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.clearPrime`, []);
3217 }
3218
3219 clearPrimeCall() {
3220 return this.helper.constructApiCall(`api.tx.${this.membership}.clearPrime`, []);
3221 }
3222}
3223
3224class RankedCollectiveGroup extends HelperGroup<UniqueHelper> {
3225 /**
3226 * Pallet name to make an API call to. Examples: 'FellowshipCollective'
3227 */
3228 private collective: string;
3229
3230 constructor(helper: UniqueHelper, collective: string) {
3231 super(helper);
3232 this.collective = collective;
3233 }
3234
3235 addMember(signer: TSigner, newMember: string) {
3236 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.addMember`, [newMember]);
3237 }
3238
3239 addMemberCall(newMember: string) {
3240 return this.helper.constructApiCall(`api.tx.${this.collective}.addMember`, [newMember]);
3241 }
3242
3243 removeMember(signer: TSigner, member: string, minRank: number) {
3244 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.removeMember`, [member, minRank]);
3245 }
3246
3247 removeMemberCall(newMember: string, minRank: number) {
3248 return this.helper.constructApiCall(`api.tx.${this.collective}.removeMember`, [newMember, minRank]);
3249 }
3250
3251 promote(signer: TSigner, member: string) {
3252 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.promoteMember`, [member]);
3253 }
3254
3255 promoteCall(member: string) {
3256 return this.helper.constructApiCall(`api.tx.${this.collective}.promoteMember`, [member]);
3257 }
3258
3259 demote(signer: TSigner, member: string) {
3260 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.demoteMember`, [member]);
3261 }
3262
3263 demoteCall(newMember: string) {
3264 return this.helper.constructApiCall(`api.tx.${this.collective}.demoteMember`, [newMember]);
3265 }
3266
3267 vote(signer: TSigner, pollIndex: number, aye: boolean) {
3268 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [pollIndex, aye]);
3269 }
3270
3271 async getMembers() {
3272 return (await this.helper.getApi().query.fellowshipCollective.members.keys())
3273 .map((key) => key.args[0].toString());
3274 }
3275
3276 async getMemberRank(member: string) {
3277 return (await this.helper.callRpc('api.query.fellowshipCollective.members', [member])).toJSON().rank;
3278 }
3279}
3280
3281class ReferendaGroup extends HelperGroup<UniqueHelper> {
3282 /**
3283 * Pallet name to make an API call to. Examples: 'FellowshipReferenda'
3284 */
3285 private referenda: string;
3286
3287 constructor(helper: UniqueHelper, referenda: string) {
3288 super(helper);
3289 this.referenda = referenda;
3290 }
3291
3292 submit(
3293 signer: TSigner,
3294 proposalOrigin: string,
3295 proposal: any,
3296 enactmentMoment: any,
3297 ) {
3298 return this.helper.executeExtrinsic(signer, `api.tx.${this.referenda}.submit`, [
3299 {Origins: proposalOrigin},
3300 proposal,
3301 enactmentMoment,
3302 ]);
3303 }
3304
3305 placeDecisionDeposit(signer: TSigner, referendumIndex: number) {
3306 return this.helper.executeExtrinsic(signer, `api.tx.${this.referenda}.placeDecisionDeposit`, [referendumIndex]);
3307 }
3308
3309 cancel(signer: TSigner, referendumIndex: number) {
3310 return this.helper.executeExtrinsic(signer, `api.tx.${this.referenda}.cancel`, [referendumIndex]);
3311 }
3312
3313 cancelCall(referendumIndex: number) {
3314 return this.helper.constructApiCall(`api.tx.${this.referenda}.cancel`, [referendumIndex]);
3315 }
3316
3317 async referendumInfo(referendumIndex: number) {
3318 return (await this.helper.callRpc(`api.query.${this.referenda}.referendumInfoFor`, [referendumIndex])).toJSON();
3319 }
3320
3321 async enactmentEventId(referendumIndex: number) {
3322 const api = await this.helper.getApi();
3323
3324 const bytes = api.createType('([u8;8], Text, u32)', ['assembly', 'enactment', referendumIndex]).toU8a();
3325 return blake2AsHex(bytes, 256);
3326 }
3327}
3328
3329export interface IFellowshipGroup {
3330 collective: RankedCollectiveGroup;
3331 referenda: ReferendaGroup;
3332}
3333
3334export interface ICollectiveGroup {
3335 collective: CollectiveGroup;
3336 membership: CollectiveMembershipGroup;
3337}
3338
3339class DemocracyGroup extends HelperGroup<UniqueHelper> {
3340 // todo displace proposal into types?
3341 propose(signer: TSigner, call: any, deposit: bigint) {
3342 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.propose', [{Inline: call.method.toHex()}, deposit]);
3343 }
3344
3345 proposeWithPreimage(signer: TSigner, preimage: string, deposit: bigint) {
3346 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.propose', [{Legacy: preimage}, deposit]);
3347 }
3348
3349 proposeCall(call: any, deposit: bigint) {
3350 return this.helper.constructApiCall('api.tx.democracy.propose', [{Inline: call.method.toHex()}, deposit]);
3351 }
3352
3353 second(signer: TSigner, proposalIndex: number) {
3354 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.second', [proposalIndex]);
3355 }
3356
3357 externalPropose(signer: TSigner, proposalCall: any) {
3358 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalPropose', [{Inline: proposalCall.method.toHex()}]);
3359 }
3360
3361 externalProposeMajority(signer: TSigner, proposalCall: any) {
3362 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalProposeMajority', [{Inline: proposalCall.method.toHex()}]);
3363 }
3364
3365 externalProposeDefault(signer: TSigner, proposalCall: any) {
3366 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalProposeDefault', [{Inline: proposalCall.method.toHex()}]);
3367 }
3368
3369 externalProposeDefaultWithPreimage(signer: TSigner, preimage: string) {
3370 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalProposeDefault', [{Legacy: preimage}]);
3371 }
3372
3373 externalProposeCall(proposalCall: any) {
3374 return this.helper.constructApiCall('api.tx.democracy.externalPropose', [{Inline: proposalCall.method.toHex()}]);
3375 }
3376
3377 externalProposeMajorityCall(proposalCall: any) {
3378 return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [{Inline: proposalCall.method.toHex()}]);
3379 }
3380
3381 externalProposeDefaultCall(proposalCall: any) {
3382 return this.helper.constructApiCall('api.tx.democracy.externalProposeDefault', [{Inline: proposalCall.method.toHex()}]);
3383 }
3384
3385 externalProposeDefaultWithPreimageCall(preimage: string) {
3386 return this.helper.constructApiCall('api.tx.democracy.externalProposeDefault', [{Legacy: preimage}]);
3387 }
3388
3389 // ... and blacklist external proposal hash.
3390 vetoExternal(signer: TSigner, proposalHash: string) {
3391 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.vetoExternal', [proposalHash]);
3392 }
3393
3394 vetoExternalCall(proposalHash: string) {
3395 return this.helper.constructApiCall('api.tx.democracy.vetoExternal', [proposalHash]);
3396 }
3397
3398 blacklist(signer: TSigner, proposalHash: string, referendumIndex: number | null = null) {
3399 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.blacklist', [proposalHash, referendumIndex]);
3400 }
3401
3402 blacklistCall(proposalHash: string, referendumIndex: number | null = null) {
3403 return this.helper.constructApiCall('api.tx.democracy.blacklist', [proposalHash, referendumIndex]);
3404 }
3405
3406 // proposal. CancelProposalOrigin (root or all techcom)
3407 cancelProposal(signer: TSigner, proposalIndex: number) {
3408 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.cancelProposal', [proposalIndex]);
3409 }
3410
3411 cancelProposalCall(proposalIndex: number) {
3412 return this.helper.constructApiCall('api.tx.democracy.cancelProposal', [proposalIndex]);
3413 }
3414
3415 clearPublicProposals(signer: TSigner) {
3416 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.clearPublicProposals', []);
3417 }
3418
3419 fastTrack(signer: TSigner, proposalHash: string, votingPeriod: number, delayPeriod: number) {
3420 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);
3421 }
3422
3423 fastTrackCall(proposalHash: string, votingPeriod: number, delayPeriod: number) {
3424 return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);
3425 }
3426
3427 // referendum. CancellationOrigin (TechCom member)
3428 emergencyCancel(signer: TSigner, referendumIndex: number) {
3429 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.emergencyCancel', [referendumIndex]);
3430 }
3431
3432 emergencyCancelCall(referendumIndex: number) {
3433 return this.helper.constructApiCall('api.tx.democracy.emergencyCancel', [referendumIndex]);
3434 }
3435
3436 vote(signer: TSigner, referendumIndex: number, vote: any) {
3437 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, vote]);
3438 }
3439
3440 removeVote(signer: TSigner, referendumIndex: number, targetAccount?: string) {
3441 if(targetAccount) {
3442 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.removeOtherVote', [targetAccount, referendumIndex]);
3443 } else {
3444 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.removeVote', [referendumIndex]);
3445 }
3446 }
3447
3448 unlock(signer: TSigner, targetAccount: string) {
3449 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.unlock', [targetAccount]);
3450 }
3451
3452 delegate(signer: TSigner, toAccount: string, conviction: PalletDemocracyConviction, balance: bigint) {
3453 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.delegate', [toAccount, conviction, balance]);
3454 }
3455
3456 undelegate(signer: TSigner) {
3457 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.undelegate', []);
3458 }
3459
3460 async referendumInfo(referendumIndex: number) {
3461 return (await this.helper.callRpc('api.query.democracy.referendumInfoOf', [referendumIndex])).toJSON();
3462 }
3463
3464 async publicProposals() {
3465 return (await this.helper.callRpc('api.query.democracy.publicProps', [])).toJSON();
3466 }
3467
3468 async findPublicProposal(proposalIndex: number) {
3469 const proposalInfo = (await this.publicProposals()).find((proposalInfo: any[]) => proposalInfo[0] == proposalIndex);
3470
3471 return proposalInfo ? proposalInfo[1] : null;
3472 }
3473
3474 async expectPublicProposal(proposalIndex: number) {
3475 const proposal = await this.findPublicProposal(proposalIndex);
3476
3477 if(proposal) {
3478 return proposal;
3479 } else {
3480 throw Error(`Proposal #${proposalIndex} is expected to exist`);
3481 }
3482 }
3483
3484 async getExternalProposal() {
3485 return (await this.helper.callRpc('api.query.democracy.nextExternal', []));
3486 }
3487
3488 async expectExternalProposal() {
3489 const proposal = await this.getExternalProposal();
3490
3491 if(proposal) {
3492 return proposal;
3493 } else {
3494 throw Error('An external proposal is expected to exist');
3495 }
3496 }
3497
3498 /* setMetadata? */
3499
3500 /* todo?
3501 referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {
3502 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);
3503 }*/
3504}
35052866
3506class PreimageGroup extends HelperGroup<UniqueHelper> {2867class PreimageGroup extends HelperGroup<UniqueHelper> {
3507 async getPreimageInfo(h256: string) {2868 async getPreimageInfo(h256: string) {
3572 }2933 }
3573}2934}
3574
3575class ForeignAssetsGroup extends HelperGroup<UniqueHelper> {
3576 async register(signer: TSigner, ownerAddress: TSubstrateAccount, location: any, metadata: IForeignAssetMetadata) {
3577 await this.helper.executeExtrinsic(
3578 signer,
3579 'api.tx.foreignAssets.registerForeignAsset',
3580 [ownerAddress, location, metadata],
3581 true,
3582 );
3583 }
3584
3585 async update(signer: TSigner, foreignAssetId: number, location: any, metadata: IForeignAssetMetadata) {
3586 await this.helper.executeExtrinsic(
3587 signer,
3588 'api.tx.foreignAssets.updateForeignAsset',
3589 [foreignAssetId, location, metadata],
3590 true,
3591 );
3592 }
3593}
3594
3595export class XcmGroup<T extends ChainHelperBase> extends HelperGroup<T> {
3596 palletName: string;
3597
3598 constructor(helper: T, palletName: string) {
3599 super(helper);
3600
3601 this.palletName = palletName;
3602 }
3603
3604 async limitedReserveTransferAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number, weightLimit: any) {
3605 await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.limitedReserveTransferAssets`, [destination, beneficiary, assets, feeAssetItem, weightLimit], true);
3606 }
3607
3608 async setSafeXcmVersion(signer: TSigner, version: number) {
3609 await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.forceDefaultXcmVersion`, [version], true);
3610 }
3611
3612 async teleportAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number) {
3613 await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.teleportAssets`, [destination, beneficiary, assets, feeAssetItem], true);
3614 }
3615
3616 async teleportNativeAsset(signer: TSigner, destinationParaId: number, targetAccount: Uint8Array, amount: bigint, xcmVersion = 3) {
3617 const destinationContent = {
3618 parents: 0,
3619 interior: {
3620 X1: {
3621 Parachain: destinationParaId,
3622 },
3623 },
3624 };
3625
3626 const beneficiaryContent = {
3627 parents: 0,
3628 interior: {
3629 X1: {
3630 AccountId32: {
3631 network: 'Any',
3632 id: targetAccount,
3633 },
3634 },
3635 },
3636 };
3637
3638 const assetsContent = [
3639 {
3640 id: {
3641 Concrete: {
3642 parents: 0,
3643 interior: 'Here',
3644 },
3645 },
3646 fun: {
3647 Fungible: amount,
3648 },
3649 },
3650 ];
3651
3652 let destination;
3653 let beneficiary;
3654 let assets;
3655
3656 if(xcmVersion == 2) {
3657 destination = {V1: destinationContent};
3658 beneficiary = {V1: beneficiaryContent};
3659 assets = {V1: assetsContent};
3660
3661 } else if(xcmVersion == 3) {
3662 destination = {V2: destinationContent};
3663 beneficiary = {V2: beneficiaryContent};
3664 assets = {V2: assetsContent};
3665
3666 } else {
3667 throw Error('Unknown XCM version: ' + xcmVersion);
3668 }
3669
3670 const feeAssetItem = 0;
3671
3672 await this.teleportAssets(signer, destination, beneficiary, assets, feeAssetItem);
3673 }
3674
3675 async send(signer: IKeyringPair, destination: any, message: any) {
3676 await this.helper.executeExtrinsic(
3677 signer,
3678 `api.tx.${this.palletName}.send`,
3679 [
3680 destination,
3681 message,
3682 ],
3683 true,
3684 );
3685 }
3686}
3687
3688export class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {
3689 async transfer(signer: TSigner, currencyId: any, amount: bigint, destination: any, destWeight: any) {
3690 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transfer', [currencyId, amount, destination, destWeight], true);
3691 }
3692
3693 async transferMultiasset(signer: TSigner, asset: any, destination: any, destWeight: any) {
3694 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMultiasset', [asset, destination, destWeight], true);
3695 }
3696
3697 async transferMulticurrencies(signer: TSigner, currencies: any[], feeItem: number, destLocation: any, destWeight: any) {
3698 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMulticurrencies', [currencies, feeItem, destLocation, destWeight], true);
3699 }
3700}
3701
3702
3703
3704export class TokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {
3705 async accounts(address: string, currencyId: any) {
3706 const {free} = (await this.helper.callRpc('api.query.tokens.accounts', [address, currencyId])).toJSON() as any;
3707 return BigInt(free);
3708 }
3709}
3710
3711export class AssetsGroup<T extends ChainHelperBase> extends HelperGroup<T> {
3712 async create(signer: TSigner, assetId: number, admin: string, minimalBalance: bigint) {
3713 await this.helper.executeExtrinsic(signer, 'api.tx.assets.create', [assetId, admin, minimalBalance], true);
3714 }
3715
3716 async setMetadata(signer: TSigner, assetId: number, name: string, symbol: string, decimals: number) {
3717 await this.helper.executeExtrinsic(signer, 'api.tx.assets.setMetadata', [assetId, name, symbol, decimals], true);
3718 }
3719
3720 async mint(signer: TSigner, assetId: number, beneficiary: string, amount: bigint) {
3721 await this.helper.executeExtrinsic(signer, 'api.tx.assets.mint', [assetId, beneficiary, amount], true);
3722 }
3723
3724 async account(assetId: string | number, address: string) {
3725 const accountAsset = (
3726 await this.helper.callRpc('api.query.assets.account', [assetId, address])
3727 ).toJSON()! as any;
3728
3729 if(accountAsset !== null) {
3730 return BigInt(accountAsset['balance']);
3731 } else {
3732 return null;
3733 }
3734 }
3735}
37362935
3737class UtilityGroup<T extends ChainHelperBase> extends HelperGroup<T> {2936class UtilityGroup<T extends ChainHelperBase> extends HelperGroup<T> {
3738 async batch(signer: TSigner, txs: any[]) {2937 async batch(signer: TSigner, txs: any[]) {
3760 rft: RFTGroup;2957 rft: RFTGroup;
3761 ft: FTGroup;2958 ft: FTGroup;
3762 staking: StakingGroup;2959 staking: StakingGroup;
3763 scheduler: SchedulerGroup;
3764 collatorSelection: CollatorSelectionGroup;
3765 council: ICollectiveGroup;
3766 technicalCommittee: ICollectiveGroup;
3767 fellowship: IFellowshipGroup;
3768 democracy: DemocracyGroup;
3769 preimage: PreimageGroup;2960 preimage: PreimageGroup;
3770 foreignAssets: ForeignAssetsGroup;
3771 xcm: XcmGroup<UniqueHelper>;
3772 xTokens: XTokensGroup<UniqueHelper>;
3773 tokens: TokensGroup<UniqueHelper>;
3774 utility: UtilityGroup<UniqueHelper>;2961 utility: UtilityGroup<UniqueHelper>;
37752962
3776 constructor(logger?: ILogger, options: { [key: string]: any } = {}) {2963 constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
3782 this.rft = new RFTGroup(this);2969 this.rft = new RFTGroup(this);
3783 this.ft = new FTGroup(this);2970 this.ft = new FTGroup(this);
3784 this.staking = new StakingGroup(this);2971 this.staking = new StakingGroup(this);
3785 this.scheduler = new SchedulerGroup(this);
3786 this.collatorSelection = new CollatorSelectionGroup(this);
3787 this.council = {
3788 collective: new CollectiveGroup(this, 'council'),
3789 membership: new CollectiveMembershipGroup(this, 'councilMembership'),
3790 };
3791 this.technicalCommittee = {
3792 collective: new CollectiveGroup(this, 'technicalCommittee'),
3793 membership: new CollectiveMembershipGroup(this, 'technicalCommitteeMembership'),
3794 };
3795 this.fellowship = {
3796 collective: new RankedCollectiveGroup(this, 'fellowshipCollective'),
3797 referenda: new ReferendaGroup(this, 'fellowshipReferenda'),
3798 };
3799 this.democracy = new DemocracyGroup(this);
3800 this.preimage = new PreimageGroup(this);2972 this.preimage = new PreimageGroup(this);
3801 this.foreignAssets = new ForeignAssetsGroup(this);
3802 this.xcm = new XcmGroup(this, 'polkadotXcm');
3803 this.xTokens = new XTokensGroup(this);
3804 this.tokens = new TokensGroup(this);
3805 this.utility = new UtilityGroup(this);2973 this.utility = new UtilityGroup(this);
3806 }2974 }
3807}2975}
3808
3809// eslint-disable-next-line @typescript-eslint/naming-convention
3810function ScheduledUniqueHelper<T extends UniqueHelperConstructor>(Base: T) {
3811 return class extends Base {
3812 scheduleFn: 'schedule' | 'scheduleAfter';
3813 blocksNum: number;
3814 options: ISchedulerOptions;
3815
3816 constructor(...args: any[]) {
3817 const logger = args[0] as ILogger;
3818 const options = args[1] as {
3819 scheduleFn: 'schedule' | 'scheduleAfter',
3820 blocksNum: number,
3821 options: ISchedulerOptions
3822 };
3823
3824 super(logger);
3825
3826 this.scheduleFn = options.scheduleFn;
3827 this.blocksNum = options.blocksNum;
3828 this.options = options.options;
3829 }
3830
3831 executeExtrinsic(sender: IKeyringPair, scheduledExtrinsic: string, scheduledParams: any[], expectSuccess?: boolean): Promise<ITransactionResult> {
3832 const scheduledTx = this.constructApiCall(scheduledExtrinsic, scheduledParams);
3833
3834 const mandatorySchedArgs = [
3835 this.blocksNum,
3836 this.options.periodic ? [this.options.periodic.period, this.options.periodic.repetitions] : null,
3837 this.options.priority ?? null,
3838 scheduledTx,
3839 ];
3840
3841 let schedArgs;
3842 let scheduleFn;
3843
3844 if(this.options.scheduledId) {
3845 schedArgs = [this.options.scheduledId!, ...mandatorySchedArgs];
3846
3847 if(this.scheduleFn == 'schedule') {
3848 scheduleFn = 'scheduleNamed';
3849 } else if(this.scheduleFn == 'scheduleAfter') {
3850 scheduleFn = 'scheduleNamedAfter';
3851 }
3852 } else {
3853 schedArgs = mandatorySchedArgs;
3854 scheduleFn = this.scheduleFn;
3855 }
3856
3857 const extrinsic = 'api.tx.scheduler.' + scheduleFn;
3858
3859 return super.executeExtrinsic(
3860 sender,
3861 extrinsic as any,
3862 schedArgs,
3863 expectSuccess,
3864 );
3865 }
3866 };
3867}
38682976
3869export class UniqueBaseCollection {2977export class UniqueBaseCollection {
3870 helper: UniqueHelper;2978 helper: UniqueHelper;
3975 return await this.helper.collection.burn(signer, this.collectionId);3083 return await this.helper.collection.burn(signer, this.collectionId);
3976 }3084 }
3977
3978 scheduleAt<T extends UniqueHelper>(
3979 executionBlockNumber: number,
3980 options: ISchedulerOptions = {},
3981 ) {
3982 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);
3983 return new UniqueBaseCollection(this.collectionId, scheduledHelper);
3984 }
3985
3986 scheduleAfter<T extends UniqueHelper>(
3987 blocksBeforeExecution: number,
3988 options: ISchedulerOptions = {},
3989 ) {
3990 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);
3991 return new UniqueBaseCollection(this.collectionId, scheduledHelper);
3992 }
3993}3085}
39943086
3995export class UniqueNFTCollection extends UniqueBaseCollection {3087export class UniqueNFTCollection extends UniqueBaseCollection {
4084 return await this.helper.nft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);3176 return await this.helper.nft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);
4085 }3177 }
4086
4087 scheduleAt<T extends UniqueHelper>(
4088 executionBlockNumber: number,
4089 options: ISchedulerOptions = {},
4090 ) {
4091 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);
4092 return new UniqueNFTCollection(this.collectionId, scheduledHelper);
4093 }
4094
4095 scheduleAfter<T extends UniqueHelper>(
4096 blocksBeforeExecution: number,
4097 options: ISchedulerOptions = {},
4098 ) {
4099 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);
4100 return new UniqueNFTCollection(this.collectionId, scheduledHelper);
4101 }
4102}3178}
41033179
4104export class UniqueRFTCollection extends UniqueBaseCollection {3180export class UniqueRFTCollection extends UniqueBaseCollection {
4205 return await this.helper.rft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);3281 return await this.helper.rft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);
4206 }3282 }
4207
4208 scheduleAt<T extends UniqueHelper>(
4209 executionBlockNumber: number,
4210 options: ISchedulerOptions = {},
4211 ) {
4212 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);
4213 return new UniqueRFTCollection(this.collectionId, scheduledHelper);
4214 }
4215
4216 scheduleAfter<T extends UniqueHelper>(
4217 blocksBeforeExecution: number,
4218 options: ISchedulerOptions = {},
4219 ) {
4220 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);
4221 return new UniqueRFTCollection(this.collectionId, scheduledHelper);
4222 }
4223}3283}
42243284
4225export class UniqueFTCollection extends UniqueBaseCollection {3285export class UniqueFTCollection extends UniqueBaseCollection {
4267 return await this.helper.ft.approveTokens(signer, this.collectionId, toAddressObj, amount);3327 return await this.helper.ft.approveTokens(signer, this.collectionId, toAddressObj, amount);
4268 }3328 }
4269
4270 scheduleAt<T extends UniqueHelper>(
4271 executionBlockNumber: number,
4272 options: ISchedulerOptions = {},
4273 ) {
4274 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);
4275 return new UniqueFTCollection(this.collectionId, scheduledHelper);
4276 }
4277
4278 scheduleAfter<T extends UniqueHelper>(
4279 blocksBeforeExecution: number,
4280 options: ISchedulerOptions = {},
4281 ) {
4282 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);
4283 return new UniqueFTCollection(this.collectionId, scheduledHelper);
4284 }
4285}3329}
42863330
4287export class UniqueBaseToken {3331export class UniqueBaseToken {
4323 return this.collection.helper.util.getTokenAccount(this);3367 return this.collection.helper.util.getTokenAccount(this);
4324 }3368 }
4325
4326 scheduleAt<T extends UniqueHelper>(
4327 executionBlockNumber: number,
4328 options: ISchedulerOptions = {},
4329 ) {
4330 const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);
4331 return new UniqueBaseToken(this.tokenId, scheduledCollection);
4332 }
4333
4334 scheduleAfter<T extends UniqueHelper>(
4335 blocksBeforeExecution: number,
4336 options: ISchedulerOptions = {},
4337 ) {
4338 const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);
4339 return new UniqueBaseToken(this.tokenId, scheduledCollection);
4340 }
4341}3369}
43423370
4343export class UniqueNFToken extends UniqueBaseToken {3371export class UniqueNFToken extends UniqueBaseToken {
4396 return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj);3424 return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj);
4397 }3425 }
4398
4399 scheduleAt<T extends UniqueHelper>(
4400 executionBlockNumber: number,
4401 options: ISchedulerOptions = {},
4402 ) {
4403 const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);
4404 return new UniqueNFToken(this.tokenId, scheduledCollection);
4405 }
4406
4407 scheduleAfter<T extends UniqueHelper>(
4408 blocksBeforeExecution: number,
4409 options: ISchedulerOptions = {},
4410 ) {
4411 const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);
4412 return new UniqueNFToken(this.tokenId, scheduledCollection);
4413 }
4414}3426}
44153427
4416export class UniqueRFToken extends UniqueBaseToken {3428export class UniqueRFToken extends UniqueBaseToken {
4481 return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj, amount);3493 return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj, amount);
4482 }3494 }
4483
4484 scheduleAt<T extends UniqueHelper>(
4485 executionBlockNumber: number,
4486 options: ISchedulerOptions = {},
4487 ) {
4488 const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);
4489 return new UniqueRFToken(this.tokenId, scheduledCollection);
4490 }
4491
4492 scheduleAfter<T extends UniqueHelper>(
4493 blocksBeforeExecution: number,
4494 options: ISchedulerOptions = {},
4495 ) {
4496 const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);
4497 return new UniqueRFToken(this.tokenId, scheduledCollection);
4498 }
4499}3495}
45003496
modifiedtests/src/util/playgrounds/unique.xcm.tsdiffbeforeafterboth
1
2import {ApiPromise, WsProvider} from '@polkadot/api';1import {ApiPromise, WsProvider} from '@polkadot/api';
2import {IKeyringPair} from '@polkadot/types/types';
3import {AssetsGroup, ChainHelperBase, EthereumBalanceGroup, HelperGroup, SubstrateBalanceGroup, TokensGroup, UniqueHelper, XTokensGroup, XcmGroup} from './unique';3import {ChainHelperBase, EthereumBalanceGroup, HelperGroup, SubstrateBalanceGroup, UniqueHelper} from './unique';
4import {ILogger, TSigner} from './types';4import {ILogger, TSigner, TSubstrateAccount} from './types';
5import {SudoHelper} from './unique.dev';
6import {AcalaAssetMetadata, DemocracyStandardAccountVote, MoonbeamAssetInfo} from './types.xcm';5import {AcalaAssetMetadata, DemocracyStandardAccountVote, IForeignAssetMetadata, MoonbeamAssetInfo} from './types.xcm';
6
77
8export class XcmChainHelper extends ChainHelperBase {8export class XcmChainHelper extends ChainHelperBase {
103 }103 }
104}104}
105
106export class ForeignAssetsGroup extends HelperGroup<UniqueHelper> {
107 async register(signer: TSigner, ownerAddress: TSubstrateAccount, location: any, metadata: IForeignAssetMetadata) {
108 await this.helper.executeExtrinsic(
109 signer,
110 'api.tx.foreignAssets.registerForeignAsset',
111 [ownerAddress, location, metadata],
112 true,
113 );
114 }
115
116 async update(signer: TSigner, foreignAssetId: number, location: any, metadata: IForeignAssetMetadata) {
117 await this.helper.executeExtrinsic(
118 signer,
119 'api.tx.foreignAssets.updateForeignAsset',
120 [foreignAssetId, location, metadata],
121 true,
122 );
123 }
124}
125
126export class XcmGroup<T extends ChainHelperBase> extends HelperGroup<T> {
127 palletName: string;
128
129 constructor(helper: T, palletName: string) {
130 super(helper);
131
132 this.palletName = palletName;
133 }
134
135 async limitedReserveTransferAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number, weightLimit: any) {
136 await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.limitedReserveTransferAssets`, [destination, beneficiary, assets, feeAssetItem, weightLimit], true);
137 }
138
139 async setSafeXcmVersion(signer: TSigner, version: number) {
140 await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.forceDefaultXcmVersion`, [version], true);
141 }
142
143 async teleportAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number) {
144 await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.teleportAssets`, [destination, beneficiary, assets, feeAssetItem], true);
145 }
146
147 async teleportNativeAsset(signer: TSigner, destinationParaId: number, targetAccount: Uint8Array, amount: bigint, xcmVersion = 3) {
148 const destinationContent = {
149 parents: 0,
150 interior: {
151 X1: {
152 Parachain: destinationParaId,
153 },
154 },
155 };
156
157 const beneficiaryContent = {
158 parents: 0,
159 interior: {
160 X1: {
161 AccountId32: {
162 network: 'Any',
163 id: targetAccount,
164 },
165 },
166 },
167 };
168
169 const assetsContent = [
170 {
171 id: {
172 Concrete: {
173 parents: 0,
174 interior: 'Here',
175 },
176 },
177 fun: {
178 Fungible: amount,
179 },
180 },
181 ];
182
183 let destination;
184 let beneficiary;
185 let assets;
186
187 if(xcmVersion == 2) {
188 destination = {V1: destinationContent};
189 beneficiary = {V1: beneficiaryContent};
190 assets = {V1: assetsContent};
191
192 } else if(xcmVersion == 3) {
193 destination = {V2: destinationContent};
194 beneficiary = {V2: beneficiaryContent};
195 assets = {V2: assetsContent};
196
197 } else {
198 throw Error('Unknown XCM version: ' + xcmVersion);
199 }
200
201 const feeAssetItem = 0;
202
203 await this.teleportAssets(signer, destination, beneficiary, assets, feeAssetItem);
204 }
205
206 async send(signer: IKeyringPair, destination: any, message: any) {
207 await this.helper.executeExtrinsic(
208 signer,
209 `api.tx.${this.palletName}.send`,
210 [
211 destination,
212 message,
213 ],
214 true,
215 );
216 }
217}
218
219export class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {
220 async transfer(signer: TSigner, currencyId: any, amount: bigint, destination: any, destWeight: any) {
221 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transfer', [currencyId, amount, destination, destWeight], true);
222 }
223
224 async transferMultiasset(signer: TSigner, asset: any, destination: any, destWeight: any) {
225 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMultiasset', [asset, destination, destWeight], true);
226 }
227
228 async transferMulticurrencies(signer: TSigner, currencies: any[], feeItem: number, destLocation: any, destWeight: any) {
229 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMulticurrencies', [currencies, feeItem, destLocation, destWeight], true);
230 }
231}
232
233
234
235export class TokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {
236 async accounts(address: string, currencyId: any) {
237 const {free} = (await this.helper.callRpc('api.query.tokens.accounts', [address, currencyId])).toJSON() as any;
238 return BigInt(free);
239 }
240}
241
242export class AssetsGroup<T extends ChainHelperBase> extends HelperGroup<T> {
243 async create(signer: TSigner, assetId: number, admin: string, minimalBalance: bigint) {
244 await this.helper.executeExtrinsic(signer, 'api.tx.assets.create', [assetId, admin, minimalBalance], true);
245 }
246
247 async setMetadata(signer: TSigner, assetId: number, name: string, symbol: string, decimals: number) {
248 await this.helper.executeExtrinsic(signer, 'api.tx.assets.setMetadata', [assetId, name, symbol, decimals], true);
249 }
250
251 async mint(signer: TSigner, assetId: number, beneficiary: string, amount: bigint) {
252 await this.helper.executeExtrinsic(signer, 'api.tx.assets.mint', [assetId, beneficiary, amount], true);
253 }
254
255 async account(assetId: string | number, address: string) {
256 const accountAsset = (
257 await this.helper.callRpc('api.query.assets.account', [assetId, address])
258 ).toJSON()! as any;
259
260 if(accountAsset !== null) {
261 return BigInt(accountAsset['balance']);
262 } else {
263 return null;
264 }
265 }
266}
267
105export class RelayHelper extends XcmChainHelper {268export class RelayHelper extends XcmChainHelper {
106 balance: SubstrateBalanceGroup<RelayHelper>;269 balance: SubstrateBalanceGroup<RelayHelper>;