difftreelog
Merge pull request #996 from UniqueNetwork/tests/playgrounds-refactor
in: master
refactor(playgorunds): rearranging the code structure
10 files changed
.envrcdiffbeforeafterboth--- a/.envrc
+++ b/.envrc
@@ -30,7 +30,7 @@
fi
echo -e "${GREEN}Baedeker env updated${RESET}"
- nginx_id=$(docker compose -f .baedeker/.bdk-env/docker-compose.yml ps --format=json | jq -r '.[] | select(.Service == "nginx") | .ID' -e)
+ nginx_id=$(docker compose -f .baedeker/.bdk-env/docker-compose.yml ps --format=json | jq -s 'flatten' | jq -r '.[] | select(.Service == "nginx") | .ID' -e)
if ! [ $? -eq 0 ]; then
echo -e "${RED}Nginx container not found${RESET}"
exit 0
tests/src/governance/util.tsdiffbeforeafterboth--- a/tests/src/governance/util.ts
+++ b/tests/src/governance/util.ts
@@ -2,6 +2,7 @@
import {xxhashAsHex} from '@polkadot/util-crypto';
import {usingPlaygrounds, expect} from '../util';
import {UniqueHelper} from '../util/playgrounds/unique';
+import {DevUniqueHelper} from '../util/playgrounds/unique.dev';
export const democracyLaunchPeriod = 35;
export const democracyVotingPeriod = 35;
@@ -203,7 +204,7 @@
});
}
-export async function voteUnanimouslyInFellowship(helper: UniqueHelper, fellows: IKeyringPair[][], minRank: number, referendumIndex: number) {
+export async function voteUnanimouslyInFellowship(helper: DevUniqueHelper, fellows: IKeyringPair[][], minRank: number, referendumIndex: number) {
for(let rank = minRank; rank < fellowshipRankLimit; rank++) {
for(const member of fellows[rank]) {
await helper.fellowship.collective.vote(member, referendumIndex, true);
tests/src/maintenance.seqtest.tsdiffbeforeafterboth--- a/tests/src/maintenance.seqtest.ts
+++ b/tests/src/maintenance.seqtest.ts
@@ -192,19 +192,22 @@
const blocksToWait = 6;
// Scheduling works before the maintenance
- await nftBeforeMM.scheduleAfter(blocksToWait, {scheduledId: scheduledIdBeforeMM})
- .transfer(bob, {Substrate: superuser.address});
+ await helper.scheduler.scheduleAfter(blocksToWait, {scheduledId: scheduledIdBeforeMM})
+ .nft.transferToken(bob, collection.collectionId, nftBeforeMM.tokenId, {Substrate: superuser.address});
+
await helper.wait.newBlocks(blocksToWait + 1);
expect(await nftBeforeMM.getOwner()).to.be.deep.equal({Substrate: superuser.address});
// Schedule a transaction that should occur *during* the maintenance
- await nftDuringMM.scheduleAfter(blocksToWait, {scheduledId: scheduledIdDuringMM})
- .transfer(bob, {Substrate: superuser.address});
+ await helper.scheduler.scheduleAfter(blocksToWait, {scheduledId: scheduledIdDuringMM})
+ .nft.transferToken(bob, collection.collectionId, nftDuringMM.tokenId, {Substrate: superuser.address});
+
// Schedule a transaction that should occur *after* the maintenance
- await nftDuringMM.scheduleAfter(blocksToWait * 2, {scheduledId: scheduledIdBunkerThroughMM})
- .transfer(bob, {Substrate: superuser.address});
+ await helper.scheduler.scheduleAfter(blocksToWait * 2, {scheduledId: scheduledIdBunkerThroughMM})
+ .nft.transferToken(bob, collection.collectionId, nftDuringMM.tokenId, {Substrate: superuser.address});
+
await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);
expect(await maintenanceEnabled(helper.getApi()), 'MM is OFF when it should be ON').to.be.true;
@@ -214,16 +217,16 @@
expect(await nftDuringMM.getOwner()).to.be.deep.equal({Substrate: bob.address});
// Any attempts to schedule a tx during the MM should be rejected
- await expect(nftDuringMM.scheduleAfter(blocksToWait, {scheduledId: scheduledIdAttemptDuringMM})
- .transfer(bob, {Substrate: superuser.address}))
+ await expect(helper.scheduler.scheduleAfter(blocksToWait, {scheduledId: scheduledIdAttemptDuringMM})
+ .nft.transferToken(bob, collection.collectionId, nftDuringMM.tokenId, {Substrate: superuser.address}))
.to.be.rejectedWith(/Invalid Transaction: Transaction call is not expected/);
await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);
expect(await maintenanceEnabled(helper.getApi()), 'MM is ON when it should be OFF').to.be.false;
// Scheduling works after the maintenance
- await nftAfterMM.scheduleAfter(blocksToWait, {scheduledId: scheduledIdAfterMM})
- .transfer(bob, {Substrate: superuser.address});
+ await helper.scheduler.scheduleAfter(blocksToWait, {scheduledId: scheduledIdAfterMM})
+ .nft.transferToken(bob, collection.collectionId, nftAfterMM.tokenId, {Substrate: superuser.address});
await helper.wait.newBlocks(blocksToWait + 1);
tests/src/scheduler.seqtest.tsdiffbeforeafterboth--- a/tests/src/scheduler.seqtest.ts
+++ b/tests/src/scheduler.seqtest.ts
@@ -47,9 +47,8 @@
const token = await collection.mintToken(alice);
const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined;
const blocksBeforeExecution = 4;
-
- await token.scheduleAfter(blocksBeforeExecution, {scheduledId})
- .transfer(alice, {Substrate: bob.address});
+ await helper.scheduler.scheduleAfter(blocksBeforeExecution, {scheduledId})
+ .nft.transferToken(alice, collection.collectionId, token.tokenId, {Substrate: bob.address});
const executionBlock = await helper.chain.getLatestBlockNumber() + blocksBeforeExecution + 1;
expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});
@@ -103,8 +102,8 @@
expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});
- await token.scheduleAfter(waitForBlocks, {scheduledId})
- .transfer(alice, {Substrate: bob.address});
+ await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId})
+ .nft.transferToken(alice, collection.collectionId, token.tokenId, {Substrate: bob.address});
const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
await helper.scheduler.cancelScheduled(alice, scheduledId);
@@ -363,8 +362,8 @@
const scheduledId = helper.arrange.makeScheduledId();
const waitForBlocks = 4;
- await token.scheduleAfter(waitForBlocks, {scheduledId})
- .transfer(bob, {Substrate: alice.address});
+ await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId})
+ .nft.transferToken(bob, collection.collectionId, token.tokenId, {Substrate: alice.address});
const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
await helper.getSudo().scheduler.cancelScheduled(superuser, scheduledId);
@@ -404,8 +403,8 @@
const scheduledId = helper.arrange.makeScheduledId();
const waitForBlocks = 6;
- await token.scheduleAfter(waitForBlocks, {scheduledId})
- .transfer(bob, {Substrate: alice.address});
+ await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId})
+ .nft.transferToken(bob, collection.collectionId, token.tokenId, {Substrate: alice.address});
const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
const priority = 112;
@@ -583,8 +582,8 @@
const scheduledId = helper.arrange.makeScheduledId();
const waitForBlocks = 4;
- await token.scheduleAfter(waitForBlocks, {scheduledId})
- .transfer(alice, {Substrate: bob.address});
+ await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId})
+ .nft.transferToken(alice, collection.collectionId, token.tokenId, {Substrate: bob.address});
const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
const scheduled = helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId});
@@ -614,8 +613,8 @@
const scheduledId = helper.arrange.makeScheduledId();
const waitForBlocks = 4;
- await token.scheduleAfter(waitForBlocks, {scheduledId})
- .transfer(alice, {Substrate: bob.address});
+ await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId})
+ .nft.transferToken(alice, collection.collectionId, token.tokenId, {Substrate: bob.address});
const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
await expect(helper.scheduler.cancelScheduled(bob, scheduledId))
@@ -655,8 +654,8 @@
const scheduledId = helper.arrange.makeScheduledId();
const waitForBlocks = 4;
- await token.scheduleAfter(waitForBlocks, {scheduledId})
- .transfer(bob, {Substrate: alice.address});
+ await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId})
+ .nft.transferToken(bob, collection.collectionId, token.tokenId, {Substrate: alice.address});
const priority = 112;
await expect(helper.scheduler.changePriority(alice, scheduledId, priority))
tests/src/util/playgrounds/types.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/types.ts
+++ b/tests/src/util/playgrounds/types.ts
@@ -216,43 +216,6 @@
},
}
-export interface IForeignAssetMetadata {
- name?: number | Uint8Array,
- symbol?: string,
- decimals?: number,
- minimalBalance?: bigint,
-}
-
-export interface MoonbeamAssetInfo {
- location: any,
- metadata: {
- name: string,
- symbol: string,
- decimals: number,
- isFrozen: boolean,
- minimalBalance: bigint,
- },
- existentialDeposit: bigint,
- isSufficient: boolean,
- unitsPerSecond: bigint,
- numAssetsWeightHint: number,
-}
-
-export interface AcalaAssetMetadata {
- name: string,
- symbol: string,
- decimals: number,
- minimalBalance: bigint,
-}
-
-export interface DemocracyStandardAccountVote {
- balance: bigint,
- vote: {
- aye: boolean,
- conviction: number,
- },
-}
-
export interface DemocracySplitAccount {
aye: bigint,
nay: bigint,
tests/src/util/playgrounds/types.xcm.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/util/playgrounds/types.xcm.ts
@@ -0,0 +1,36 @@
+export interface AcalaAssetMetadata {
+ name: string,
+ symbol: string,
+ decimals: number,
+ minimalBalance: bigint,
+}
+
+export interface MoonbeamAssetInfo {
+ location: any,
+ metadata: {
+ name: string,
+ symbol: string,
+ decimals: number,
+ isFrozen: boolean,
+ minimalBalance: bigint,
+ },
+ existentialDeposit: bigint,
+ isSufficient: boolean,
+ unitsPerSecond: bigint,
+ numAssetsWeightHint: number,
+}
+
+export interface DemocracyStandardAccountVote {
+ balance: bigint,
+ vote: {
+ aye: boolean,
+ conviction: number,
+ },
+}
+
+export interface IForeignAssetMetadata {
+ name?: number | Uint8Array,
+ symbol?: string,
+ decimals?: number,
+ minimalBalance?: bigint,
+}
\ No newline at end of file
tests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/unique.dev.ts
+++ b/tests/src/util/playgrounds/unique.dev.ts
@@ -3,16 +3,18 @@
import {stringToU8a} from '@polkadot/util';
import {blake2AsHex, encodeAddress, mnemonicGenerate} from '@polkadot/util-crypto';
-import {UniqueHelper, MoonbeamHelper, ChainHelperBase, AcalaHelper, RelayHelper, WestmintHelper, AstarHelper, PolkadexHelper} from './unique';
+import {UniqueHelper, ChainHelperBase, ChainHelperBaseConstructor, HelperGroup, UniqueHelperConstructor} from './unique';
import {ApiPromise, Keyring, WsProvider} from '@polkadot/api';
import * as defs from '../../interfaces/definitions';
import {IKeyringPair} from '@polkadot/types/types';
import {EventRecord} from '@polkadot/types/interfaces';
-import {ICrossAccountId, IPovInfo, ITransactionResult, TSigner} from './types';
+import {ICrossAccountId, ILogger, IPovInfo, ISchedulerOptions, ITransactionResult, TSigner} from './types';
import {FrameSystemEventRecord, XcmV2TraitsError} from '@polkadot/types/lookup';
-import {VoidFn} from '@polkadot/api/types';
+import {SignerOptions, VoidFn} from '@polkadot/api/types';
import {Pallets} from '..';
import {spawnSync} from 'child_process';
+import {AcalaHelper, AstarHelper, MoonbeamHelper, PolkadexHelper, RelayHelper, WestmintHelper, ForeignAssetsGroup, XcmGroup, XTokensGroup, TokensGroup} from './unique.xcm';
+import {CollectiveGroup, CollectiveMembershipGroup, DemocracyGroup, ICollectiveGroup, IFellowshipGroup, RankedCollectiveGroup, ReferendaGroup} from './unique.governance';
export class SilentLogger {
log(_msg: any, _level: any): void { }
@@ -260,6 +262,192 @@
};
}
+// eslint-disable-next-line @typescript-eslint/naming-convention
+export function SudoHelper<T extends ChainHelperBaseConstructor>(Base: T) {
+ return class extends Base {
+ constructor(...args: any[]) {
+ super(...args);
+ }
+
+ async executeExtrinsic(
+ sender: IKeyringPair,
+ extrinsic: string,
+ params: any[],
+ expectSuccess?: boolean,
+ options: Partial<SignerOptions> | null = null,
+ ): Promise<ITransactionResult> {
+ const call = this.constructApiCall(extrinsic, params);
+ const result = await super.executeExtrinsic(
+ sender,
+ 'api.tx.sudo.sudo',
+ [call],
+ expectSuccess,
+ options,
+ );
+
+ if(result.status === 'Fail') return result;
+
+ const data = (result.result.events.find(x => x.event.section == 'sudo' && x.event.method == 'Sudid')?.event.data as any).sudoResult;
+ if(data.isErr) {
+ if(data.asErr.isModule) {
+ const error = (result.result.events[1].event.data as any).sudoResult.asErr.asModule;
+ const metaError = super.getApi()?.registry.findMetaError(error);
+ throw new Error(`${metaError.section}.${metaError.name}`);
+ } else if(data.asErr.isToken) {
+ throw new Error(`Token: ${data.asErr.asToken}`);
+ }
+ // May be [object Object] in case of unhandled non-unit enum
+ throw new Error(`Misc: ${data.asErr.toHuman()}`);
+ }
+ return result;
+ }
+ async executeExtrinsicUncheckedWeight(
+ sender: IKeyringPair,
+ extrinsic: string,
+ params: any[],
+ expectSuccess?: boolean,
+ options: Partial<SignerOptions> | null = null,
+ ): Promise<ITransactionResult> {
+ const call = this.constructApiCall(extrinsic, params);
+ const result = await super.executeExtrinsic(
+ sender,
+ 'api.tx.sudo.sudoUncheckedWeight',
+ [call, {refTime: 0, proofSize: 0}],
+ expectSuccess,
+ options,
+ );
+
+ if(result.status === 'Fail') return result;
+
+ const data = (result.result.events.find(x => x.event.section == 'sudo' && x.event.method == 'Sudid')?.event.data as any).sudoResult;
+ if(data.isErr) {
+ if(data.asErr.isModule) {
+ const error = (result.result.events[1].event.data as any).sudoResult.asErr.asModule;
+ const metaError = super.getApi()?.registry.findMetaError(error);
+ throw new Error(`${metaError.section}.${metaError.name}`);
+ } else if(data.asErr.isToken) {
+ throw new Error(`Token: ${data.asErr.asToken}`);
+ }
+ // May be [object Object] in case of unhandled non-unit enum
+ throw new Error(`Misc: ${data.asErr.toHuman()}`);
+ }
+ return result;
+ }
+ };
+}
+
+class SchedulerGroup extends HelperGroup<UniqueHelper> {
+ constructor(helper: UniqueHelper) {
+ super(helper);
+ }
+
+ cancelScheduled(signer: TSigner, scheduledId: string) {
+ return this.helper.executeExtrinsic(
+ signer,
+ 'api.tx.scheduler.cancelNamed',
+ [scheduledId],
+ true,
+ );
+ }
+
+ changePriority(signer: TSigner, scheduledId: string, priority: number) {
+ return this.helper.executeExtrinsic(
+ signer,
+ 'api.tx.scheduler.changeNamedPriority',
+ [scheduledId, priority],
+ true,
+ );
+ }
+
+ scheduleAt<T extends DevUniqueHelper>(
+ executionBlockNumber: number,
+ options: ISchedulerOptions = {},
+ ) {
+ return this.schedule<T>('schedule', executionBlockNumber, options);
+ }
+
+ scheduleAfter<T extends DevUniqueHelper>(
+ blocksBeforeExecution: number,
+ options: ISchedulerOptions = {},
+ ) {
+ return this.schedule<T>('scheduleAfter', blocksBeforeExecution, options);
+ }
+
+ schedule<T extends UniqueHelper>(
+ scheduleFn: 'schedule' | 'scheduleAfter',
+ blocksNum: number,
+ options: ISchedulerOptions = {},
+ ) {
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ const ScheduledHelperType = ScheduledUniqueHelper(this.helper.helperBase);
+ return this.helper.clone(ScheduledHelperType, {
+ scheduleFn,
+ blocksNum,
+ options,
+ }) as T;
+ }
+}
+
+class CollatorSelectionGroup extends HelperGroup<UniqueHelper> {
+ //todo:collator documentation
+ addInvulnerable(signer: TSigner, address: string) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.addInvulnerable', [address]);
+ }
+
+ removeInvulnerable(signer: TSigner, address: string) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.removeInvulnerable', [address]);
+ }
+
+ async getInvulnerables(): Promise<string[]> {
+ return (await this.helper.callRpc('api.query.collatorSelection.invulnerables')).map((x: any) => x.toHuman());
+ }
+
+ /** and also total max invulnerables */
+ maxCollators(): number {
+ return (this.helper.getApi().consts.configuration.defaultCollatorSelectionMaxCollators.toJSON() as number);
+ }
+
+ async getDesiredCollators(): Promise<number> {
+ return (await this.helper.callRpc('api.query.configuration.collatorSelectionDesiredCollatorsOverride')).toNumber();
+ }
+
+ setLicenseBond(signer: TSigner, amount: bigint) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.configuration.setCollatorSelectionLicenseBond', [amount]);
+ }
+
+ async getLicenseBond(): Promise<bigint> {
+ return (await this.helper.callRpc('api.query.configuration.collatorSelectionLicenseBondOverride')).toBigInt();
+ }
+
+ obtainLicense(signer: TSigner) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.getLicense', []);
+ }
+
+ releaseLicense(signer: TSigner) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.releaseLicense', []);
+ }
+
+ forceReleaseLicense(signer: TSigner, released: string) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.forceReleaseLicense', [released]);
+ }
+
+ async hasLicense(address: string): Promise<bigint> {
+ return (await this.helper.callRpc('api.query.collatorSelection.licenseDepositOf', [address])).toBigInt();
+ }
+
+ onboard(signer: TSigner) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.onboard', []);
+ }
+
+ offboard(signer: TSigner) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.offboard', []);
+ }
+
+ async getCandidates(): Promise<string[]> {
+ return (await this.helper.callRpc('api.query.collatorSelection.candidates')).map((x: any) => x.toHuman());
+ }
+}
+
export class DevUniqueHelper extends UniqueHelper {
/**
* Arrange methods for tests
@@ -269,6 +457,16 @@
admin: AdminGroup;
session: SessionGroup;
testUtils: TestUtilGroup;
+ foreignAssets: ForeignAssetsGroup;
+ xcm: XcmGroup<UniqueHelper>;
+ xTokens: XTokensGroup<UniqueHelper>;
+ tokens: TokensGroup<UniqueHelper>;
+ scheduler: SchedulerGroup;
+ collatorSelection: CollatorSelectionGroup;
+ council: ICollectiveGroup;
+ technicalCommittee: ICollectiveGroup;
+ fellowship: IFellowshipGroup;
+ democracy: DemocracyGroup;
constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
options.helperBase = options.helperBase ?? DevUniqueHelper;
@@ -279,6 +477,25 @@
this.admin = new AdminGroup(this);
this.testUtils = new TestUtilGroup(this);
this.session = new SessionGroup(this);
+ this.foreignAssets = new ForeignAssetsGroup(this);
+ this.xcm = new XcmGroup(this, 'polkadotXcm');
+ this.xTokens = new XTokensGroup(this);
+ this.tokens = new TokensGroup(this);
+ this.scheduler = new SchedulerGroup(this);
+ this.collatorSelection = new CollatorSelectionGroup(this);
+ this.council = {
+ collective: new CollectiveGroup(this, 'council'),
+ membership: new CollectiveMembershipGroup(this, 'councilMembership'),
+ };
+ this.technicalCommittee = {
+ collective: new CollectiveGroup(this, 'technicalCommittee'),
+ membership: new CollectiveMembershipGroup(this, 'technicalCommitteeMembership'),
+ };
+ this.fellowship = {
+ collective: new RankedCollectiveGroup(this, 'fellowshipCollective'),
+ referenda: new ReferendaGroup(this, 'fellowshipReferenda'),
+ };
+ this.democracy = new DemocracyGroup(this);
}
async connect(wsEndpoint: string, _listeners?: any): Promise<void> {
@@ -326,6 +543,11 @@
this.network = await UniqueHelper.detectNetwork(this.api);
this.wsEndpoint = wsEndpoint;
}
+ getSudo<T extends DevUniqueHelper>() {
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ const SudoHelperType = SudoHelper(this.helperBase);
+ return this.clone(SudoHelperType) as T;
+ }
}
export class DevRelayHelper extends RelayHelper {
@@ -386,19 +608,16 @@
super(logger, options);
this.wait = new WaitGroup(this);
}
-}
-
-export class DevShidenHelper extends AstarHelper {
- wait: WaitGroup;
- constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
- options.helperBase = options.helperBase ?? DevShidenHelper;
-
- super(logger, options);
- this.wait = new WaitGroup(this);
+ getSudo<T extends AstarHelper>() {
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ const SudoHelperType = SudoHelper(this.helperBase);
+ return this.clone(SudoHelperType) as T;
}
}
+export class DevShidenHelper extends DevAstarHelper { }
+
export class DevAcalaHelper extends AcalaHelper {
wait: WaitGroup;
@@ -408,6 +627,11 @@
super(logger, options);
this.wait = new WaitGroup(this);
}
+ getSudo<T extends AcalaHelper>() {
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ const SudoHelperType = SudoHelper(this.helperBase);
+ return this.clone(SudoHelperType) as T;
+ }
}
export class DevPolkadexHelper extends PolkadexHelper {
@@ -418,6 +642,12 @@
super(logger, options);
this.wait = new WaitGroup(this);
}
+
+ getSudo<T extends PolkadexHelper>() {
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ const SudoHelperType = SudoHelper(this.helperBase);
+ return this.clone(SudoHelperType) as T;
+ }
}
export class DevKaruraHelper extends DevAcalaHelper {}
@@ -1211,3 +1441,63 @@
}));
}
}
+
+// eslint-disable-next-line @typescript-eslint/naming-convention
+function ScheduledUniqueHelper<T extends UniqueHelperConstructor>(Base: T) {
+ return class extends Base {
+ scheduleFn: 'schedule' | 'scheduleAfter';
+ blocksNum: number;
+ options: ISchedulerOptions;
+
+ constructor(...args: any[]) {
+ const logger = args[0] as ILogger;
+ const options = args[1] as {
+ scheduleFn: 'schedule' | 'scheduleAfter',
+ blocksNum: number,
+ options: ISchedulerOptions
+ };
+
+ super(logger);
+
+ this.scheduleFn = options.scheduleFn;
+ this.blocksNum = options.blocksNum;
+ this.options = options.options;
+ }
+
+ executeExtrinsic(sender: IKeyringPair, scheduledExtrinsic: string, scheduledParams: any[], expectSuccess?: boolean): Promise<ITransactionResult> {
+ const scheduledTx = this.constructApiCall(scheduledExtrinsic, scheduledParams);
+
+ const mandatorySchedArgs = [
+ this.blocksNum,
+ this.options.periodic ? [this.options.periodic.period, this.options.periodic.repetitions] : null,
+ this.options.priority ?? null,
+ scheduledTx,
+ ];
+
+ let schedArgs;
+ let scheduleFn;
+
+ if(this.options.scheduledId) {
+ schedArgs = [this.options.scheduledId!, ...mandatorySchedArgs];
+
+ if(this.scheduleFn == 'schedule') {
+ scheduleFn = 'scheduleNamed';
+ } else if(this.scheduleFn == 'scheduleAfter') {
+ scheduleFn = 'scheduleNamedAfter';
+ }
+ } else {
+ schedArgs = mandatorySchedArgs;
+ scheduleFn = this.scheduleFn;
+ }
+
+ const extrinsic = 'api.tx.scheduler.' + scheduleFn;
+
+ return super.executeExtrinsic(
+ sender,
+ extrinsic as any,
+ schedArgs,
+ expectSuccess,
+ );
+ }
+ };
+}
\ No newline at end of file
tests/src/util/playgrounds/unique.governance.tsdiffbeforeafterboth1import {blake2AsHex} from '@polkadot/util-crypto';2import {PalletDemocracyConviction} from '@polkadot/types/lookup';3import {IPhasicEvent, TSigner} from './types';4import {HelperGroup, UniqueHelper} from './unique';56export class CollectiveGroup extends HelperGroup<UniqueHelper> {7 /**8 * Pallet name to make an API call to. Examples: 'council', 'technicalCommittee'9 */10 private collective: string;1112 constructor(helper: UniqueHelper, collective: string) {13 super(helper);14 this.collective = collective;15 }1617 /**18 * Check the result of a proposal execution for the success of the underlying proposed extrinsic.19 * @param events events of the proposal execution20 * @returns proposal hash21 */22 private checkExecutedEvent(events: IPhasicEvent[]): string {23 const executionEvents = events.filter(x =>24 x.event.section === this.collective && (x.event.method === 'Executed' || x.event.method === 'MemberExecuted'));2526 if(executionEvents.length != 1) {27 if(events.filter(x => x.event.section === this.collective && x.event.method === 'Disapproved').length > 0)28 throw new Error(`Disapproved by ${this.collective}`);29 else30 throw new Error(`Expected one 'Executed' or 'MemberExecuted' event for ${this.collective}`);31 }3233 const result = (executionEvents[0].event.data as any).result;3435 if(result.isErr) {36 if(result.asErr.isModule) {37 const error = result.asErr.asModule;38 const metaError = this.helper.getApi()?.registry.findMetaError(error);39 throw new Error(`Proposal execution failed with ${metaError.section}.${metaError.name}`);40 } else {41 throw new Error('Proposal execution failed with ' + result.asErr.toHuman());42 }43 }4445 return (executionEvents[0].event.data as any).proposalHash;46 }4748 /**49 * Returns an array of members' addresses.50 */51 async getMembers() {52 return (await this.helper.callRpc(`api.query.${this.collective}.members`, [])).toHuman();53 }5455 /**56 * Returns the optional address of the prime member of the collective.57 */58 async getPrimeMember() {59 return (await this.helper.callRpc(`api.query.${this.collective}.prime`, [])).toHuman();60 }6162 /**63 * Returns an array of proposal hashes that are currently active for this collective.64 */65 async getProposals() {66 return (await this.helper.callRpc(`api.query.${this.collective}.proposals`, [])).toHuman();67 }6869 /**70 * Returns the call originally encoded under the specified hash.71 * @param hash h256-encoded proposal72 * @returns the optional call that the proposal hash stands for.73 */74 async getProposalCallOf(hash: string) {75 return (await this.helper.callRpc(`api.query.${this.collective}.proposalOf`, [hash])).toHuman();76 }7778 /**79 * Returns the total number of proposals so far.80 */81 async getTotalProposalsCount() {82 return (await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, [])).toNumber();83 }8485 /**86 * Creates a new proposal up for voting. If the threshold is set to 1, the proposal will be executed immediately.87 * @param signer keyring of the proposer88 * @param proposal constructed call to be executed if the proposal is successful89 * @param voteThreshold minimal number of votes for the proposal to be verified and executed90 * @param lengthBound byte length of the encoded call91 * @returns promise of extrinsic execution and its result92 */93 async propose(signer: TSigner, proposal: any, voteThreshold: number, lengthBound = 10000) {94 return await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [voteThreshold, proposal, lengthBound]);95 }9697 /**98 * Casts a vote to either approve or reject a proposal.99 * @param signer keyring of the voter100 * @param proposalHash hash of the proposal to be voted for101 * @param proposalIndex absolute index of the proposal used for absolutely nothing but throwing pointless errors102 * @param approve aye or nay103 * @returns promise of extrinsic execution and its result104 */105 vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {106 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve]);107 }108109 /**110 * Executes a call immediately as a member of the collective. Needed for the Member origin.111 * @param signer keyring of the executor member112 * @param proposal constructed call to be executed by the member113 * @param lengthBound byte length of the encoded call114 * @returns promise of extrinsic execution115 */116 async execute(signer: TSigner, proposal: any, lengthBound = 10000) {117 const result = await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.execute`, [proposal, lengthBound]);118 this.checkExecutedEvent(result.result.events);119 return result;120 }121122 /**123 * Attempt to close and execute a proposal. Note that there must already be enough votes to meet the threshold set when proposing.124 * @param signer keyring of the executor. Can be absolutely anyone.125 * @param proposalHash hash of the proposal to close126 * @param proposalIndex index of the proposal generated on its creation127 * @param weightBound weight of the proposed call. Can be obtained by calling `paymentInfo()` on the call.128 * @param lengthBound byte length of the encoded call129 * @returns promise of extrinsic execution and its result130 */131 async close(132 signer: TSigner,133 proposalHash: string,134 proposalIndex: number,135 weightBound: [number, number] | any = [20_000_000_000, 1000_000],136 lengthBound = 10_000,137 ) {138 const result = await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [139 proposalHash,140 proposalIndex,141 weightBound,142 lengthBound,143 ]);144 this.checkExecutedEvent(result.result.events);145 return result;146 }147148 /**149 * Shut down a proposal, regardless of its current state.150 * @param signer keyring of the disapprover. Must be root151 * @param proposalHash hash of the proposal to close152 * @returns promise of extrinsic execution and its result153 */154 disapproveProposal(signer: TSigner, proposalHash: string) {155 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.disapproveProposal`, [proposalHash]);156 }157}158159export class CollectiveMembershipGroup extends HelperGroup<UniqueHelper> {160 /**161 * Pallet name to make an API call to. Examples: 'councilMembership', 'technicalCommitteeMembership'162 */163 private membership: string;164165 constructor(helper: UniqueHelper, membership: string) {166 super(helper);167 this.membership = membership;168 }169170 /**171 * Returns an array of members' addresses according to the membership pallet's perception.172 * Note that it does not recognize the original pallet's members set with `setMembers()`.173 */174 async getMembers() {175 return (await this.helper.callRpc(`api.query.${this.membership}.members`, [])).toHuman();176 }177178 /**179 * Returns the optional address of the prime member of the collective.180 */181 async getPrimeMember() {182 return (await this.helper.callRpc(`api.query.${this.membership}.prime`, [])).toHuman();183 }184185 /**186 * Add a member to the collective.187 * @param signer keyring of the setter. Must be root188 * @param member address of the member to add189 * @returns promise of extrinsic execution and its result190 */191 addMember(signer: TSigner, member: string) {192 return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.addMember`, [member]);193 }194195 addMemberCall(member: string) {196 return this.helper.constructApiCall(`api.tx.${this.membership}.addMember`, [member]);197 }198199 /**200 * Remove a member from the collective.201 * @param signer keyring of the setter. Must be root202 * @param member address of the member to remove203 * @returns promise of extrinsic execution and its result204 */205 removeMember(signer: TSigner, member: string) {206 return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.removeMember`, [member]);207 }208209 removeMemberCall(member: string) {210 return this.helper.constructApiCall(`api.tx.${this.membership}.removeMember`, [member]);211 }212213 /**214 * Set members of the collective to the given list of addresses.215 * @param signer keyring of the setter. Must be root (for the direct call, bypassing a public motion)216 * @param members addresses of the members to set217 * @returns promise of extrinsic execution and its result218 */219 resetMembers(signer: TSigner, members: string[]) {220 return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.resetMembers`, [members]);221 }222223 /**224 * Set the collective's prime member to the given address.225 * @param signer keyring of the setter. Must be root (for the direct call, bypassing a public motion)226 * @param prime address of the prime member of the collective227 * @returns promise of extrinsic execution and its result228 */229 setPrime(signer: TSigner, prime: string) {230 return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.setPrime`, [prime]);231 }232233 setPrimeCall(member: string) {234 return this.helper.constructApiCall(`api.tx.${this.membership}.setPrime`, [member]);235 }236237 /**238 * Remove the collective's prime member.239 * @param signer keyring of the setter. Must be root (for the direct call, bypassing a public motion)240 * @returns promise of extrinsic execution and its result241 */242 clearPrime(signer: TSigner) {243 return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.clearPrime`, []);244 }245246 clearPrimeCall() {247 return this.helper.constructApiCall(`api.tx.${this.membership}.clearPrime`, []);248 }249}250251export class RankedCollectiveGroup extends HelperGroup<UniqueHelper> {252 /**253 * Pallet name to make an API call to. Examples: 'FellowshipCollective'254 */255 private collective: string;256257 constructor(helper: UniqueHelper, collective: string) {258 super(helper);259 this.collective = collective;260 }261262 addMember(signer: TSigner, newMember: string) {263 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.addMember`, [newMember]);264 }265266 addMemberCall(newMember: string) {267 return this.helper.constructApiCall(`api.tx.${this.collective}.addMember`, [newMember]);268 }269270 removeMember(signer: TSigner, member: string, minRank: number) {271 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.removeMember`, [member, minRank]);272 }273274 removeMemberCall(newMember: string, minRank: number) {275 return this.helper.constructApiCall(`api.tx.${this.collective}.removeMember`, [newMember, minRank]);276 }277278 promote(signer: TSigner, member: string) {279 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.promoteMember`, [member]);280 }281282 promoteCall(member: string) {283 return this.helper.constructApiCall(`api.tx.${this.collective}.promoteMember`, [member]);284 }285286 demote(signer: TSigner, member: string) {287 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.demoteMember`, [member]);288 }289290 demoteCall(newMember: string) {291 return this.helper.constructApiCall(`api.tx.${this.collective}.demoteMember`, [newMember]);292 }293294 vote(signer: TSigner, pollIndex: number, aye: boolean) {295 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [pollIndex, aye]);296 }297298 async getMembers() {299 return (await this.helper.getApi().query.fellowshipCollective.members.keys())300 .map((key) => key.args[0].toString());301 }302303 async getMemberRank(member: string) {304 return (await this.helper.callRpc('api.query.fellowshipCollective.members', [member])).toJSON().rank;305 }306}307308export class ReferendaGroup extends HelperGroup<UniqueHelper> {309 /**310 * Pallet name to make an API call to. Examples: 'FellowshipReferenda'311 */312 private referenda: string;313314 constructor(helper: UniqueHelper, referenda: string) {315 super(helper);316 this.referenda = referenda;317 }318319 submit(320 signer: TSigner,321 proposalOrigin: string,322 proposal: any,323 enactmentMoment: any,324 ) {325 return this.helper.executeExtrinsic(signer, `api.tx.${this.referenda}.submit`, [326 {Origins: proposalOrigin},327 proposal,328 enactmentMoment,329 ]);330 }331332 placeDecisionDeposit(signer: TSigner, referendumIndex: number) {333 return this.helper.executeExtrinsic(signer, `api.tx.${this.referenda}.placeDecisionDeposit`, [referendumIndex]);334 }335336 cancel(signer: TSigner, referendumIndex: number) {337 return this.helper.executeExtrinsic(signer, `api.tx.${this.referenda}.cancel`, [referendumIndex]);338 }339340 cancelCall(referendumIndex: number) {341 return this.helper.constructApiCall(`api.tx.${this.referenda}.cancel`, [referendumIndex]);342 }343344 async referendumInfo(referendumIndex: number) {345 return (await this.helper.callRpc(`api.query.${this.referenda}.referendumInfoFor`, [referendumIndex])).toJSON();346 }347348 async enactmentEventId(referendumIndex: number) {349 const api = await this.helper.getApi();350351 const bytes = api.createType('([u8;8], Text, u32)', ['assembly', 'enactment', referendumIndex]).toU8a();352 return blake2AsHex(bytes, 256);353 }354}355356export interface IFellowshipGroup {357 collective: RankedCollectiveGroup;358 referenda: ReferendaGroup;359}360361export interface ICollectiveGroup {362 collective: CollectiveGroup;363 membership: CollectiveMembershipGroup;364}365366export class DemocracyGroup extends HelperGroup<UniqueHelper> {367 // todo displace proposal into types?368 propose(signer: TSigner, call: any, deposit: bigint) {369 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.propose', [{Inline: call.method.toHex()}, deposit]);370 }371372 proposeWithPreimage(signer: TSigner, preimage: string, deposit: bigint) {373 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.propose', [{Legacy: preimage}, deposit]);374 }375376 proposeCall(call: any, deposit: bigint) {377 return this.helper.constructApiCall('api.tx.democracy.propose', [{Inline: call.method.toHex()}, deposit]);378 }379380 second(signer: TSigner, proposalIndex: number) {381 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.second', [proposalIndex]);382 }383384 externalPropose(signer: TSigner, proposalCall: any) {385 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalPropose', [{Inline: proposalCall.method.toHex()}]);386 }387388 externalProposeMajority(signer: TSigner, proposalCall: any) {389 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalProposeMajority', [{Inline: proposalCall.method.toHex()}]);390 }391392 externalProposeDefault(signer: TSigner, proposalCall: any) {393 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalProposeDefault', [{Inline: proposalCall.method.toHex()}]);394 }395396 externalProposeDefaultWithPreimage(signer: TSigner, preimage: string) {397 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalProposeDefault', [{Legacy: preimage}]);398 }399400 externalProposeCall(proposalCall: any) {401 return this.helper.constructApiCall('api.tx.democracy.externalPropose', [{Inline: proposalCall.method.toHex()}]);402 }403404 externalProposeMajorityCall(proposalCall: any) {405 return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [{Inline: proposalCall.method.toHex()}]);406 }407408 externalProposeDefaultCall(proposalCall: any) {409 return this.helper.constructApiCall('api.tx.democracy.externalProposeDefault', [{Inline: proposalCall.method.toHex()}]);410 }411412 externalProposeDefaultWithPreimageCall(preimage: string) {413 return this.helper.constructApiCall('api.tx.democracy.externalProposeDefault', [{Legacy: preimage}]);414 }415416 // ... and blacklist external proposal hash.417 vetoExternal(signer: TSigner, proposalHash: string) {418 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.vetoExternal', [proposalHash]);419 }420421 vetoExternalCall(proposalHash: string) {422 return this.helper.constructApiCall('api.tx.democracy.vetoExternal', [proposalHash]);423 }424425 blacklist(signer: TSigner, proposalHash: string, referendumIndex: number | null = null) {426 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.blacklist', [proposalHash, referendumIndex]);427 }428429 blacklistCall(proposalHash: string, referendumIndex: number | null = null) {430 return this.helper.constructApiCall('api.tx.democracy.blacklist', [proposalHash, referendumIndex]);431 }432433 // proposal. CancelProposalOrigin (root or all techcom)434 cancelProposal(signer: TSigner, proposalIndex: number) {435 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.cancelProposal', [proposalIndex]);436 }437438 cancelProposalCall(proposalIndex: number) {439 return this.helper.constructApiCall('api.tx.democracy.cancelProposal', [proposalIndex]);440 }441442 clearPublicProposals(signer: TSigner) {443 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.clearPublicProposals', []);444 }445446 fastTrack(signer: TSigner, proposalHash: string, votingPeriod: number, delayPeriod: number) {447 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);448 }449450 fastTrackCall(proposalHash: string, votingPeriod: number, delayPeriod: number) {451 return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);452 }453454 // referendum. CancellationOrigin (TechCom member)455 emergencyCancel(signer: TSigner, referendumIndex: number) {456 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.emergencyCancel', [referendumIndex]);457 }458459 emergencyCancelCall(referendumIndex: number) {460 return this.helper.constructApiCall('api.tx.democracy.emergencyCancel', [referendumIndex]);461 }462463 vote(signer: TSigner, referendumIndex: number, vote: any) {464 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, vote]);465 }466467 removeVote(signer: TSigner, referendumIndex: number, targetAccount?: string) {468 if(targetAccount) {469 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.removeOtherVote', [targetAccount, referendumIndex]);470 } else {471 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.removeVote', [referendumIndex]);472 }473 }474475 unlock(signer: TSigner, targetAccount: string) {476 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.unlock', [targetAccount]);477 }478479 delegate(signer: TSigner, toAccount: string, conviction: PalletDemocracyConviction, balance: bigint) {480 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.delegate', [toAccount, conviction, balance]);481 }482483 undelegate(signer: TSigner) {484 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.undelegate', []);485 }486487 async referendumInfo(referendumIndex: number) {488 return (await this.helper.callRpc('api.query.democracy.referendumInfoOf', [referendumIndex])).toJSON();489 }490491 async publicProposals() {492 return (await this.helper.callRpc('api.query.democracy.publicProps', [])).toJSON();493 }494495 async findPublicProposal(proposalIndex: number) {496 const proposalInfo = (await this.publicProposals()).find((proposalInfo: any[]) => proposalInfo[0] == proposalIndex);497498 return proposalInfo ? proposalInfo[1] : null;499 }500501 async expectPublicProposal(proposalIndex: number) {502 const proposal = await this.findPublicProposal(proposalIndex);503504 if(proposal) {505 return proposal;506 } else {507 throw Error(`Proposal #${proposalIndex} is expected to exist`);508 }509 }510511 async getExternalProposal() {512 return (await this.helper.callRpc('api.query.democracy.nextExternal', []));513 }514515 async expectExternalProposal() {516 const proposal = await this.getExternalProposal();517518 if(proposal) {519 return proposal;520 } else {521 throw Error('An external proposal is expected to exist');522 }523 }524525 /* setMetadata? */526527 /* todo?528 referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {529 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);530 }*/531}tests/src/util/playgrounds/unique.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/unique.ts
+++ b/tests/src/util/playgrounds/unique.ts
@@ -39,16 +39,11 @@
TSigner,
TSubstrateAccount,
TNetworks,
- IForeignAssetMetadata,
- AcalaAssetMetadata,
- MoonbeamAssetInfo,
- DemocracyStandardAccountVote,
IEthCrossAccountId,
- IPhasicEvent,
} from './types';
import {RuntimeDispatchInfo} from '@polkadot/types/interfaces';
import type {Vec} from '@polkadot/types-codec';
-import {FrameSystemEventRecord, PalletDemocracyConviction} from '@polkadot/types/lookup';
+import {FrameSystemEventRecord} from '@polkadot/types/lookup';
export class CrossAccountId {
Substrate!: TSubstrateAccount;
@@ -818,7 +813,7 @@
}
-class HelperGroup<T extends ChainHelperBase> {
+export class HelperGroup<T extends ChainHelperBase> {
helper: T;
constructor(uniqueHelper: T) {
@@ -2373,7 +2368,7 @@
}
}
-class SubstrateBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {
+export class SubstrateBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {
/**
* Get substrate address balance
* @param address substrate address
@@ -2440,7 +2435,7 @@
}
}
-class EthereumBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {
+export class EthereumBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {
/**
* Get ethereum address balance
* @param address ethereum address
@@ -2760,6 +2755,7 @@
}
}
+
class StakingGroup extends HelperGroup<UniqueHelper> {
/**
* Stake tokens for App Promotion
@@ -2867,645 +2863,7 @@
}
}
-class SchedulerGroup extends HelperGroup<UniqueHelper> {
- constructor(helper: UniqueHelper) {
- super(helper);
- }
-
- cancelScheduled(signer: TSigner, scheduledId: string) {
- return this.helper.executeExtrinsic(
- signer,
- 'api.tx.scheduler.cancelNamed',
- [scheduledId],
- true,
- );
- }
-
- changePriority(signer: TSigner, scheduledId: string, priority: number) {
- return this.helper.executeExtrinsic(
- signer,
- 'api.tx.scheduler.changeNamedPriority',
- [scheduledId, priority],
- true,
- );
- }
-
- scheduleAt<T extends UniqueHelper>(
- executionBlockNumber: number,
- options: ISchedulerOptions = {},
- ) {
- return this.schedule<T>('schedule', executionBlockNumber, options);
- }
- scheduleAfter<T extends UniqueHelper>(
- blocksBeforeExecution: number,
- options: ISchedulerOptions = {},
- ) {
- return this.schedule<T>('scheduleAfter', blocksBeforeExecution, options);
- }
-
- schedule<T extends UniqueHelper>(
- scheduleFn: 'schedule' | 'scheduleAfter',
- blocksNum: number,
- options: ISchedulerOptions = {},
- ) {
- // eslint-disable-next-line @typescript-eslint/naming-convention
- const ScheduledHelperType = ScheduledUniqueHelper(this.helper.helperBase);
- return this.helper.clone(ScheduledHelperType, {
- scheduleFn,
- blocksNum,
- options,
- }) as T;
- }
-}
-
-class CollatorSelectionGroup extends HelperGroup<UniqueHelper> {
- //todo:collator documentation
- addInvulnerable(signer: TSigner, address: string) {
- return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.addInvulnerable', [address]);
- }
-
- removeInvulnerable(signer: TSigner, address: string) {
- return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.removeInvulnerable', [address]);
- }
-
- async getInvulnerables(): Promise<string[]> {
- return (await this.helper.callRpc('api.query.collatorSelection.invulnerables')).map((x: any) => x.toHuman());
- }
-
- /** and also total max invulnerables */
- maxCollators(): number {
- return (this.helper.getApi().consts.configuration.defaultCollatorSelectionMaxCollators.toJSON() as number);
- }
-
- async getDesiredCollators(): Promise<number> {
- return (await this.helper.callRpc('api.query.configuration.collatorSelectionDesiredCollatorsOverride')).toNumber();
- }
-
- setLicenseBond(signer: TSigner, amount: bigint) {
- return this.helper.executeExtrinsic(signer, 'api.tx.configuration.setCollatorSelectionLicenseBond', [amount]);
- }
-
- async getLicenseBond(): Promise<bigint> {
- return (await this.helper.callRpc('api.query.configuration.collatorSelectionLicenseBondOverride')).toBigInt();
- }
-
- obtainLicense(signer: TSigner) {
- return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.getLicense', []);
- }
-
- releaseLicense(signer: TSigner) {
- return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.releaseLicense', []);
- }
-
- forceReleaseLicense(signer: TSigner, released: string) {
- return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.forceReleaseLicense', [released]);
- }
-
- async hasLicense(address: string): Promise<bigint> {
- return (await this.helper.callRpc('api.query.collatorSelection.licenseDepositOf', [address])).toBigInt();
- }
-
- onboard(signer: TSigner) {
- return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.onboard', []);
- }
-
- offboard(signer: TSigner) {
- return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.offboard', []);
- }
-
- async getCandidates(): Promise<string[]> {
- return (await this.helper.callRpc('api.query.collatorSelection.candidates')).map((x: any) => x.toHuman());
- }
-}
-
-class CollectiveGroup extends HelperGroup<UniqueHelper> {
- /**
- * Pallet name to make an API call to. Examples: 'council', 'technicalCommittee'
- */
- private collective: string;
-
- constructor(helper: UniqueHelper, collective: string) {
- super(helper);
- this.collective = collective;
- }
-
- /**
- * Check the result of a proposal execution for the success of the underlying proposed extrinsic.
- * @param events events of the proposal execution
- * @returns proposal hash
- */
- private checkExecutedEvent(events: IPhasicEvent[]): string {
- const executionEvents = events.filter(x =>
- x.event.section === this.collective && (x.event.method === 'Executed' || x.event.method === 'MemberExecuted'));
-
- if(executionEvents.length != 1) {
- if(events.filter(x => x.event.section === this.collective && x.event.method === 'Disapproved').length > 0)
- throw new Error(`Disapproved by ${this.collective}`);
- else
- throw new Error(`Expected one 'Executed' or 'MemberExecuted' event for ${this.collective}`);
- }
-
- const result = (executionEvents[0].event.data as any).result;
-
- if(result.isErr) {
- if(result.asErr.isModule) {
- const error = result.asErr.asModule;
- const metaError = this.helper.getApi()?.registry.findMetaError(error);
- throw new Error(`Proposal execution failed with ${metaError.section}.${metaError.name}`);
- } else {
- throw new Error('Proposal execution failed with ' + result.asErr.toHuman());
- }
- }
-
- return (executionEvents[0].event.data as any).proposalHash;
- }
-
- /**
- * Returns an array of members' addresses.
- */
- async getMembers() {
- return (await this.helper.callRpc(`api.query.${this.collective}.members`, [])).toHuman();
- }
-
- /**
- * Returns the optional address of the prime member of the collective.
- */
- async getPrimeMember() {
- return (await this.helper.callRpc(`api.query.${this.collective}.prime`, [])).toHuman();
- }
-
- /**
- * Returns an array of proposal hashes that are currently active for this collective.
- */
- async getProposals() {
- return (await this.helper.callRpc(`api.query.${this.collective}.proposals`, [])).toHuman();
- }
-
- /**
- * Returns the call originally encoded under the specified hash.
- * @param hash h256-encoded proposal
- * @returns the optional call that the proposal hash stands for.
- */
- async getProposalCallOf(hash: string) {
- return (await this.helper.callRpc(`api.query.${this.collective}.proposalOf`, [hash])).toHuman();
- }
-
- /**
- * Returns the total number of proposals so far.
- */
- async getTotalProposalsCount() {
- return (await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, [])).toNumber();
- }
-
- /**
- * Creates a new proposal up for voting. If the threshold is set to 1, the proposal will be executed immediately.
- * @param signer keyring of the proposer
- * @param proposal constructed call to be executed if the proposal is successful
- * @param voteThreshold minimal number of votes for the proposal to be verified and executed
- * @param lengthBound byte length of the encoded call
- * @returns promise of extrinsic execution and its result
- */
- async propose(signer: TSigner, proposal: any, voteThreshold: number, lengthBound = 10000) {
- return await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [voteThreshold, proposal, lengthBound]);
- }
-
- /**
- * Casts a vote to either approve or reject a proposal.
- * @param signer keyring of the voter
- * @param proposalHash hash of the proposal to be voted for
- * @param proposalIndex absolute index of the proposal used for absolutely nothing but throwing pointless errors
- * @param approve aye or nay
- * @returns promise of extrinsic execution and its result
- */
- vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve]);
- }
-
- /**
- * Executes a call immediately as a member of the collective. Needed for the Member origin.
- * @param signer keyring of the executor member
- * @param proposal constructed call to be executed by the member
- * @param lengthBound byte length of the encoded call
- * @returns promise of extrinsic execution
- */
- async execute(signer: TSigner, proposal: any, lengthBound = 10000) {
- const result = await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.execute`, [proposal, lengthBound]);
- this.checkExecutedEvent(result.result.events);
- return result;
- }
-
- /**
- * Attempt to close and execute a proposal. Note that there must already be enough votes to meet the threshold set when proposing.
- * @param signer keyring of the executor. Can be absolutely anyone.
- * @param proposalHash hash of the proposal to close
- * @param proposalIndex index of the proposal generated on its creation
- * @param weightBound weight of the proposed call. Can be obtained by calling `paymentInfo()` on the call.
- * @param lengthBound byte length of the encoded call
- * @returns promise of extrinsic execution and its result
- */
- async close(
- signer: TSigner,
- proposalHash: string,
- proposalIndex: number,
- weightBound: [number, number] | any = [20_000_000_000, 1000_000],
- lengthBound = 10_000,
- ) {
- const result = await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [
- proposalHash,
- proposalIndex,
- weightBound,
- lengthBound,
- ]);
- this.checkExecutedEvent(result.result.events);
- return result;
- }
-
- /**
- * Shut down a proposal, regardless of its current state.
- * @param signer keyring of the disapprover. Must be root
- * @param proposalHash hash of the proposal to close
- * @returns promise of extrinsic execution and its result
- */
- disapproveProposal(signer: TSigner, proposalHash: string) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.disapproveProposal`, [proposalHash]);
- }
-}
-
-class CollectiveMembershipGroup extends HelperGroup<UniqueHelper> {
- /**
- * Pallet name to make an API call to. Examples: 'councilMembership', 'technicalCommitteeMembership'
- */
- private membership: string;
-
- constructor(helper: UniqueHelper, membership: string) {
- super(helper);
- this.membership = membership;
- }
-
- /**
- * Returns an array of members' addresses according to the membership pallet's perception.
- * Note that it does not recognize the original pallet's members set with `setMembers()`.
- */
- async getMembers() {
- return (await this.helper.callRpc(`api.query.${this.membership}.members`, [])).toHuman();
- }
-
- /**
- * Returns the optional address of the prime member of the collective.
- */
- async getPrimeMember() {
- return (await this.helper.callRpc(`api.query.${this.membership}.prime`, [])).toHuman();
- }
-
- /**
- * Add a member to the collective.
- * @param signer keyring of the setter. Must be root
- * @param member address of the member to add
- * @returns promise of extrinsic execution and its result
- */
- addMember(signer: TSigner, member: string) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.addMember`, [member]);
- }
-
- addMemberCall(member: string) {
- return this.helper.constructApiCall(`api.tx.${this.membership}.addMember`, [member]);
- }
-
- /**
- * Remove a member from the collective.
- * @param signer keyring of the setter. Must be root
- * @param member address of the member to remove
- * @returns promise of extrinsic execution and its result
- */
- removeMember(signer: TSigner, member: string) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.removeMember`, [member]);
- }
-
- removeMemberCall(member: string) {
- return this.helper.constructApiCall(`api.tx.${this.membership}.removeMember`, [member]);
- }
-
- /**
- * Set members of the collective to the given list of addresses.
- * @param signer keyring of the setter. Must be root (for the direct call, bypassing a public motion)
- * @param members addresses of the members to set
- * @returns promise of extrinsic execution and its result
- */
- resetMembers(signer: TSigner, members: string[]) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.resetMembers`, [members]);
- }
-
- /**
- * Set the collective's prime member to the given address.
- * @param signer keyring of the setter. Must be root (for the direct call, bypassing a public motion)
- * @param prime address of the prime member of the collective
- * @returns promise of extrinsic execution and its result
- */
- setPrime(signer: TSigner, prime: string) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.setPrime`, [prime]);
- }
-
- setPrimeCall(member: string) {
- return this.helper.constructApiCall(`api.tx.${this.membership}.setPrime`, [member]);
- }
-
- /**
- * Remove the collective's prime member.
- * @param signer keyring of the setter. Must be root (for the direct call, bypassing a public motion)
- * @returns promise of extrinsic execution and its result
- */
- clearPrime(signer: TSigner) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.clearPrime`, []);
- }
-
- clearPrimeCall() {
- return this.helper.constructApiCall(`api.tx.${this.membership}.clearPrime`, []);
- }
-}
-
-class RankedCollectiveGroup extends HelperGroup<UniqueHelper> {
- /**
- * Pallet name to make an API call to. Examples: 'FellowshipCollective'
- */
- private collective: string;
-
- constructor(helper: UniqueHelper, collective: string) {
- super(helper);
- this.collective = collective;
- }
-
- addMember(signer: TSigner, newMember: string) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.addMember`, [newMember]);
- }
-
- addMemberCall(newMember: string) {
- return this.helper.constructApiCall(`api.tx.${this.collective}.addMember`, [newMember]);
- }
-
- removeMember(signer: TSigner, member: string, minRank: number) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.removeMember`, [member, minRank]);
- }
-
- removeMemberCall(newMember: string, minRank: number) {
- return this.helper.constructApiCall(`api.tx.${this.collective}.removeMember`, [newMember, minRank]);
- }
-
- promote(signer: TSigner, member: string) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.promoteMember`, [member]);
- }
-
- promoteCall(member: string) {
- return this.helper.constructApiCall(`api.tx.${this.collective}.promoteMember`, [member]);
- }
-
- demote(signer: TSigner, member: string) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.demoteMember`, [member]);
- }
-
- demoteCall(newMember: string) {
- return this.helper.constructApiCall(`api.tx.${this.collective}.demoteMember`, [newMember]);
- }
-
- vote(signer: TSigner, pollIndex: number, aye: boolean) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [pollIndex, aye]);
- }
-
- async getMembers() {
- return (await this.helper.getApi().query.fellowshipCollective.members.keys())
- .map((key) => key.args[0].toString());
- }
-
- async getMemberRank(member: string) {
- return (await this.helper.callRpc('api.query.fellowshipCollective.members', [member])).toJSON().rank;
- }
-}
-
-class ReferendaGroup extends HelperGroup<UniqueHelper> {
- /**
- * Pallet name to make an API call to. Examples: 'FellowshipReferenda'
- */
- private referenda: string;
-
- constructor(helper: UniqueHelper, referenda: string) {
- super(helper);
- this.referenda = referenda;
- }
-
- submit(
- signer: TSigner,
- proposalOrigin: string,
- proposal: any,
- enactmentMoment: any,
- ) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.referenda}.submit`, [
- {Origins: proposalOrigin},
- proposal,
- enactmentMoment,
- ]);
- }
-
- placeDecisionDeposit(signer: TSigner, referendumIndex: number) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.referenda}.placeDecisionDeposit`, [referendumIndex]);
- }
-
- cancel(signer: TSigner, referendumIndex: number) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.referenda}.cancel`, [referendumIndex]);
- }
-
- cancelCall(referendumIndex: number) {
- return this.helper.constructApiCall(`api.tx.${this.referenda}.cancel`, [referendumIndex]);
- }
-
- async referendumInfo(referendumIndex: number) {
- return (await this.helper.callRpc(`api.query.${this.referenda}.referendumInfoFor`, [referendumIndex])).toJSON();
- }
-
- async enactmentEventId(referendumIndex: number) {
- const api = await this.helper.getApi();
-
- const bytes = api.createType('([u8;8], Text, u32)', ['assembly', 'enactment', referendumIndex]).toU8a();
- return blake2AsHex(bytes, 256);
- }
-}
-
-export interface IFellowshipGroup {
- collective: RankedCollectiveGroup;
- referenda: ReferendaGroup;
-}
-
-export interface ICollectiveGroup {
- collective: CollectiveGroup;
- membership: CollectiveMembershipGroup;
-}
-
-class DemocracyGroup extends HelperGroup<UniqueHelper> {
- // todo displace proposal into types?
- propose(signer: TSigner, call: any, deposit: bigint) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.propose', [{Inline: call.method.toHex()}, deposit]);
- }
-
- proposeWithPreimage(signer: TSigner, preimage: string, deposit: bigint) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.propose', [{Legacy: preimage}, deposit]);
- }
-
- proposeCall(call: any, deposit: bigint) {
- return this.helper.constructApiCall('api.tx.democracy.propose', [{Inline: call.method.toHex()}, deposit]);
- }
-
- second(signer: TSigner, proposalIndex: number) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.second', [proposalIndex]);
- }
-
- externalPropose(signer: TSigner, proposalCall: any) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalPropose', [{Inline: proposalCall.method.toHex()}]);
- }
-
- externalProposeMajority(signer: TSigner, proposalCall: any) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalProposeMajority', [{Inline: proposalCall.method.toHex()}]);
- }
-
- externalProposeDefault(signer: TSigner, proposalCall: any) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalProposeDefault', [{Inline: proposalCall.method.toHex()}]);
- }
-
- externalProposeDefaultWithPreimage(signer: TSigner, preimage: string) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalProposeDefault', [{Legacy: preimage}]);
- }
-
- externalProposeCall(proposalCall: any) {
- return this.helper.constructApiCall('api.tx.democracy.externalPropose', [{Inline: proposalCall.method.toHex()}]);
- }
-
- externalProposeMajorityCall(proposalCall: any) {
- return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [{Inline: proposalCall.method.toHex()}]);
- }
-
- externalProposeDefaultCall(proposalCall: any) {
- return this.helper.constructApiCall('api.tx.democracy.externalProposeDefault', [{Inline: proposalCall.method.toHex()}]);
- }
-
- externalProposeDefaultWithPreimageCall(preimage: string) {
- return this.helper.constructApiCall('api.tx.democracy.externalProposeDefault', [{Legacy: preimage}]);
- }
-
- // ... and blacklist external proposal hash.
- vetoExternal(signer: TSigner, proposalHash: string) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.vetoExternal', [proposalHash]);
- }
-
- vetoExternalCall(proposalHash: string) {
- return this.helper.constructApiCall('api.tx.democracy.vetoExternal', [proposalHash]);
- }
-
- blacklist(signer: TSigner, proposalHash: string, referendumIndex: number | null = null) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.blacklist', [proposalHash, referendumIndex]);
- }
-
- blacklistCall(proposalHash: string, referendumIndex: number | null = null) {
- return this.helper.constructApiCall('api.tx.democracy.blacklist', [proposalHash, referendumIndex]);
- }
-
- // proposal. CancelProposalOrigin (root or all techcom)
- cancelProposal(signer: TSigner, proposalIndex: number) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.cancelProposal', [proposalIndex]);
- }
-
- cancelProposalCall(proposalIndex: number) {
- return this.helper.constructApiCall('api.tx.democracy.cancelProposal', [proposalIndex]);
- }
-
- clearPublicProposals(signer: TSigner) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.clearPublicProposals', []);
- }
-
- fastTrack(signer: TSigner, proposalHash: string, votingPeriod: number, delayPeriod: number) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);
- }
-
- fastTrackCall(proposalHash: string, votingPeriod: number, delayPeriod: number) {
- return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);
- }
-
- // referendum. CancellationOrigin (TechCom member)
- emergencyCancel(signer: TSigner, referendumIndex: number) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.emergencyCancel', [referendumIndex]);
- }
-
- emergencyCancelCall(referendumIndex: number) {
- return this.helper.constructApiCall('api.tx.democracy.emergencyCancel', [referendumIndex]);
- }
-
- vote(signer: TSigner, referendumIndex: number, vote: any) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, vote]);
- }
-
- removeVote(signer: TSigner, referendumIndex: number, targetAccount?: string) {
- if(targetAccount) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.removeOtherVote', [targetAccount, referendumIndex]);
- } else {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.removeVote', [referendumIndex]);
- }
- }
-
- unlock(signer: TSigner, targetAccount: string) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.unlock', [targetAccount]);
- }
-
- delegate(signer: TSigner, toAccount: string, conviction: PalletDemocracyConviction, balance: bigint) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.delegate', [toAccount, conviction, balance]);
- }
-
- undelegate(signer: TSigner) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.undelegate', []);
- }
-
- async referendumInfo(referendumIndex: number) {
- return (await this.helper.callRpc('api.query.democracy.referendumInfoOf', [referendumIndex])).toJSON();
- }
-
- async publicProposals() {
- return (await this.helper.callRpc('api.query.democracy.publicProps', [])).toJSON();
- }
-
- async findPublicProposal(proposalIndex: number) {
- const proposalInfo = (await this.publicProposals()).find((proposalInfo: any[]) => proposalInfo[0] == proposalIndex);
-
- return proposalInfo ? proposalInfo[1] : null;
- }
-
- async expectPublicProposal(proposalIndex: number) {
- const proposal = await this.findPublicProposal(proposalIndex);
-
- if(proposal) {
- return proposal;
- } else {
- throw Error(`Proposal #${proposalIndex} is expected to exist`);
- }
- }
-
- async getExternalProposal() {
- return (await this.helper.callRpc('api.query.democracy.nextExternal', []));
- }
-
- async expectExternalProposal() {
- const proposal = await this.getExternalProposal();
-
- if(proposal) {
- return proposal;
- } else {
- throw Error('An external proposal is expected to exist');
- }
- }
-
- /* setMetadata? */
-
- /* todo?
- referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);
- }*/
-}
-
class PreimageGroup extends HelperGroup<UniqueHelper> {
async getPreimageInfo(h256: string) {
return (await this.helper.callRpc('api.query.preimage.statusFor', [h256])).toJSON();
@@ -3572,173 +2930,7 @@
*/
unrequestPreimage(signer: TSigner, h256: string) {
return this.helper.executeExtrinsic(signer, 'api.tx.preimage.unrequestPreimage', [h256]);
- }
-}
-
-class ForeignAssetsGroup extends HelperGroup<UniqueHelper> {
- async register(signer: TSigner, ownerAddress: TSubstrateAccount, location: any, metadata: IForeignAssetMetadata) {
- await this.helper.executeExtrinsic(
- signer,
- 'api.tx.foreignAssets.registerForeignAsset',
- [ownerAddress, location, metadata],
- true,
- );
- }
-
- async update(signer: TSigner, foreignAssetId: number, location: any, metadata: IForeignAssetMetadata) {
- await this.helper.executeExtrinsic(
- signer,
- 'api.tx.foreignAssets.updateForeignAsset',
- [foreignAssetId, location, metadata],
- true,
- );
- }
-}
-
-class XcmGroup<T extends ChainHelperBase> extends HelperGroup<T> {
- palletName: string;
-
- constructor(helper: T, palletName: string) {
- super(helper);
-
- this.palletName = palletName;
- }
-
- async limitedReserveTransferAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number, weightLimit: any) {
- await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.limitedReserveTransferAssets`, [destination, beneficiary, assets, feeAssetItem, weightLimit], true);
- }
-
- async setSafeXcmVersion(signer: TSigner, version: number) {
- await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.forceDefaultXcmVersion`, [version], true);
}
-
- async teleportAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number) {
- await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.teleportAssets`, [destination, beneficiary, assets, feeAssetItem], true);
- }
-
- async teleportNativeAsset(signer: TSigner, destinationParaId: number, targetAccount: Uint8Array, amount: bigint, xcmVersion = 3) {
- const destinationContent = {
- parents: 0,
- interior: {
- X1: {
- Parachain: destinationParaId,
- },
- },
- };
-
- const beneficiaryContent = {
- parents: 0,
- interior: {
- X1: {
- AccountId32: {
- network: 'Any',
- id: targetAccount,
- },
- },
- },
- };
-
- const assetsContent = [
- {
- id: {
- Concrete: {
- parents: 0,
- interior: 'Here',
- },
- },
- fun: {
- Fungible: amount,
- },
- },
- ];
-
- let destination;
- let beneficiary;
- let assets;
-
- if(xcmVersion == 2) {
- destination = {V1: destinationContent};
- beneficiary = {V1: beneficiaryContent};
- assets = {V1: assetsContent};
-
- } else if(xcmVersion == 3) {
- destination = {V2: destinationContent};
- beneficiary = {V2: beneficiaryContent};
- assets = {V2: assetsContent};
-
- } else {
- throw Error('Unknown XCM version: ' + xcmVersion);
- }
-
- const feeAssetItem = 0;
-
- await this.teleportAssets(signer, destination, beneficiary, assets, feeAssetItem);
- }
-
- async send(signer: IKeyringPair, destination: any, message: any) {
- await this.helper.executeExtrinsic(
- signer,
- `api.tx.${this.palletName}.send`,
- [
- destination,
- message,
- ],
- true,
- );
- }
-}
-
-class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {
- async transfer(signer: TSigner, currencyId: any, amount: bigint, destination: any, destWeight: any) {
- await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transfer', [currencyId, amount, destination, destWeight], true);
- }
-
- async transferMultiasset(signer: TSigner, asset: any, destination: any, destWeight: any) {
- await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMultiasset', [asset, destination, destWeight], true);
- }
-
- async transferMulticurrencies(signer: TSigner, currencies: any[], feeItem: number, destLocation: any, destWeight: any) {
- await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMulticurrencies', [currencies, feeItem, destLocation, destWeight], true);
- }
-}
-
-class PolkadexXcmHelperGroup<T extends ChainHelperBase> extends HelperGroup<T> {
- async whitelistToken(signer: TSigner, assetId: any) {
- await this.helper.executeExtrinsic(signer, 'api.tx.xcmHelper.whitelistToken', [assetId], true);
- }
-}
-
-class TokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {
- async accounts(address: string, currencyId: any) {
- const {free} = (await this.helper.callRpc('api.query.tokens.accounts', [address, currencyId])).toJSON() as any;
- return BigInt(free);
- }
-}
-
-class AssetsGroup<T extends ChainHelperBase> extends HelperGroup<T> {
- async create(signer: TSigner, assetId: number, admin: string, minimalBalance: bigint) {
- await this.helper.executeExtrinsic(signer, 'api.tx.assets.create', [assetId, admin, minimalBalance], true);
- }
-
- async setMetadata(signer: TSigner, assetId: number, name: string, symbol: string, decimals: number) {
- await this.helper.executeExtrinsic(signer, 'api.tx.assets.setMetadata', [assetId, name, symbol, decimals], true);
- }
-
- async mint(signer: TSigner, assetId: number, beneficiary: string, amount: bigint) {
- await this.helper.executeExtrinsic(signer, 'api.tx.assets.mint', [assetId, beneficiary, amount], true);
- }
-
- async account(assetId: string | number, address: string) {
- const accountAsset = (
- await this.helper.callRpc('api.query.assets.account', [assetId, address])
- ).toJSON()! as any;
-
- if(accountAsset !== null) {
- return BigInt(accountAsset['balance']);
- } else {
- return null;
- }
- }
}
class UtilityGroup<T extends ChainHelperBase> extends HelperGroup<T> {
@@ -3752,90 +2944,9 @@
batchAllCall(txs: any[]) {
return this.helper.constructApiCall('api.tx.utility.batchAll', [txs]);
- }
-}
-
-class AcalaAssetRegistryGroup extends HelperGroup<AcalaHelper> {
- async registerForeignAsset(signer: TSigner, destination: any, metadata: AcalaAssetMetadata) {
- await this.helper.executeExtrinsic(signer, 'api.tx.assetRegistry.registerForeignAsset', [destination, metadata], true);
}
}
-class MoonbeamAssetManagerGroup extends HelperGroup<MoonbeamHelper> {
- makeRegisterForeignAssetProposal(assetInfo: MoonbeamAssetInfo) {
- const apiPrefix = 'api.tx.assetManager.';
-
- const registerTx = this.helper.constructApiCall(
- apiPrefix + 'registerForeignAsset',
- [assetInfo.location, assetInfo.metadata, assetInfo.existentialDeposit, assetInfo.isSufficient],
- );
-
- const setUnitsTx = this.helper.constructApiCall(
- apiPrefix + 'setAssetUnitsPerSecond',
- [assetInfo.location, assetInfo.unitsPerSecond, assetInfo.numAssetsWeightHint],
- );
-
- const batchCall = this.helper.getApi().tx.utility.batchAll([registerTx, setUnitsTx]);
- const encodedProposal = batchCall?.method.toHex() || '';
- return encodedProposal;
- }
-
- async assetTypeId(location: any) {
- return await this.helper.callRpc('api.query.assetManager.assetTypeId', [location]);
- }
-}
-
-class MoonbeamDemocracyGroup extends HelperGroup<MoonbeamHelper> {
- notePreimagePallet: string;
-
- constructor(helper: MoonbeamHelper, options: { [key: string]: any } = {}) {
- super(helper);
- this.notePreimagePallet = options.notePreimagePallet;
- }
-
- async notePreimage(signer: TSigner, encodedProposal: string) {
- await this.helper.executeExtrinsic(signer, `api.tx.${this.notePreimagePallet}.notePreimage`, [encodedProposal], true);
- }
-
- externalProposeMajority(proposal: any) {
- return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposal]);
- }
-
- fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) {
- return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);
- }
-
- async referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {
- await this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);
- }
-}
-
-class MoonbeamCollectiveGroup extends HelperGroup<MoonbeamHelper> {
- collective: string;
-
- constructor(helper: MoonbeamHelper, collective: string) {
- super(helper);
-
- this.collective = collective;
- }
-
- async propose(signer: TSigner, threshold: number, proposalHash: string, lengthBound: number) {
- await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [threshold, proposalHash, lengthBound], true);
- }
-
- async vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {
- await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true);
- }
-
- async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: any, lengthBound: number) {
- await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true);
- }
-
- async proposalCount() {
- return Number(await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, []));
- }
-}
-
export type ChainHelperBaseConstructor = new (...args: any[]) => ChainHelperBase;
export type UniqueHelperConstructor = new (...args: any[]) => UniqueHelper;
@@ -3846,17 +2957,7 @@
rft: RFTGroup;
ft: FTGroup;
staking: StakingGroup;
- scheduler: SchedulerGroup;
- collatorSelection: CollatorSelectionGroup;
- council: ICollectiveGroup;
- technicalCommittee: ICollectiveGroup;
- fellowship: IFellowshipGroup;
- democracy: DemocracyGroup;
preimage: PreimageGroup;
- foreignAssets: ForeignAssetsGroup;
- xcm: XcmGroup<UniqueHelper>;
- xTokens: XTokensGroup<UniqueHelper>;
- tokens: TokensGroup<UniqueHelper>;
utility: UtilityGroup<UniqueHelper>;
constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
@@ -3868,303 +2969,11 @@
this.rft = new RFTGroup(this);
this.ft = new FTGroup(this);
this.staking = new StakingGroup(this);
- this.scheduler = new SchedulerGroup(this);
- this.collatorSelection = new CollatorSelectionGroup(this);
- this.council = {
- collective: new CollectiveGroup(this, 'council'),
- membership: new CollectiveMembershipGroup(this, 'councilMembership'),
- };
- this.technicalCommittee = {
- collective: new CollectiveGroup(this, 'technicalCommittee'),
- membership: new CollectiveMembershipGroup(this, 'technicalCommitteeMembership'),
- };
- this.fellowship = {
- collective: new RankedCollectiveGroup(this, 'fellowshipCollective'),
- referenda: new ReferendaGroup(this, 'fellowshipReferenda'),
- };
- this.democracy = new DemocracyGroup(this);
this.preimage = new PreimageGroup(this);
- this.foreignAssets = new ForeignAssetsGroup(this);
- this.xcm = new XcmGroup(this, 'polkadotXcm');
- this.xTokens = new XTokensGroup(this);
- this.tokens = new TokensGroup(this);
this.utility = new UtilityGroup(this);
- }
-
- getSudo<T extends UniqueHelper>() {
- // eslint-disable-next-line @typescript-eslint/naming-convention
- const SudoHelperType = SudoHelper(this.helperBase);
- return this.clone(SudoHelperType) as T;
- }
-}
-
-export class XcmChainHelper extends ChainHelperBase {
- async connect(wsEndpoint: string, _listeners?: any): Promise<void> {
- const wsProvider = new WsProvider(wsEndpoint);
- this.api = new ApiPromise({
- provider: wsProvider,
- });
- await this.api.isReadyOrError;
- this.network = await UniqueHelper.detectNetwork(this.api);
- }
-}
-
-export class RelayHelper extends XcmChainHelper {
- balance: SubstrateBalanceGroup<RelayHelper>;
- xcm: XcmGroup<RelayHelper>;
-
- constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
- super(logger, options.helperBase ?? RelayHelper);
-
- this.balance = new SubstrateBalanceGroup(this);
- this.xcm = new XcmGroup(this, 'xcmPallet');
- }
-}
-
-export class WestmintHelper extends XcmChainHelper {
- balance: SubstrateBalanceGroup<WestmintHelper>;
- xcm: XcmGroup<WestmintHelper>;
- assets: AssetsGroup<WestmintHelper>;
- xTokens: XTokensGroup<WestmintHelper>;
-
- constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
- super(logger, options.helperBase ?? WestmintHelper);
-
- this.balance = new SubstrateBalanceGroup(this);
- this.xcm = new XcmGroup(this, 'polkadotXcm');
- this.assets = new AssetsGroup(this);
- this.xTokens = new XTokensGroup(this);
- }
-}
-
-export class MoonbeamHelper extends XcmChainHelper {
- balance: EthereumBalanceGroup<MoonbeamHelper>;
- assetManager: MoonbeamAssetManagerGroup;
- assets: AssetsGroup<MoonbeamHelper>;
- xTokens: XTokensGroup<MoonbeamHelper>;
- democracy: MoonbeamDemocracyGroup;
- collective: {
- council: MoonbeamCollectiveGroup,
- techCommittee: MoonbeamCollectiveGroup,
- };
-
- constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
- super(logger, options.helperBase ?? MoonbeamHelper);
-
- this.balance = new EthereumBalanceGroup(this);
- this.assetManager = new MoonbeamAssetManagerGroup(this);
- this.assets = new AssetsGroup(this);
- this.xTokens = new XTokensGroup(this);
- this.democracy = new MoonbeamDemocracyGroup(this, options);
- this.collective = {
- council: new MoonbeamCollectiveGroup(this, 'councilCollective'),
- techCommittee: new MoonbeamCollectiveGroup(this, 'techCommitteeCollective'),
- };
}
}
-export class AstarHelper extends XcmChainHelper {
- balance: SubstrateBalanceGroup<AstarHelper>;
- assets: AssetsGroup<AstarHelper>;
- xcm: XcmGroup<AstarHelper>;
-
- constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
- super(logger, options.helperBase ?? AstarHelper);
-
- this.balance = new SubstrateBalanceGroup(this);
- this.assets = new AssetsGroup(this);
- this.xcm = new XcmGroup(this, 'polkadotXcm');
- }
-
- getSudo<T extends UniqueHelper>() {
- // eslint-disable-next-line @typescript-eslint/naming-convention
- const SudoHelperType = SudoHelper(this.helperBase);
- return this.clone(SudoHelperType) as T;
- }
-}
-
-export class AcalaHelper extends XcmChainHelper {
- balance: SubstrateBalanceGroup<AcalaHelper>;
- assetRegistry: AcalaAssetRegistryGroup;
- xTokens: XTokensGroup<AcalaHelper>;
- tokens: TokensGroup<AcalaHelper>;
- xcm: XcmGroup<AcalaHelper>;
-
- constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
- super(logger, options.helperBase ?? AcalaHelper);
-
- this.balance = new SubstrateBalanceGroup(this);
- this.assetRegistry = new AcalaAssetRegistryGroup(this);
- this.xTokens = new XTokensGroup(this);
- this.tokens = new TokensGroup(this);
- this.xcm = new XcmGroup(this, 'polkadotXcm');
- }
-
- getSudo<T extends AcalaHelper>() {
- // eslint-disable-next-line @typescript-eslint/naming-convention
- const SudoHelperType = SudoHelper(this.helperBase);
- return this.clone(SudoHelperType) as T;
- }
-}
-
-export class PolkadexHelper extends XcmChainHelper {
- assets: AssetsGroup<PolkadexHelper>;
- balance: SubstrateBalanceGroup<PolkadexHelper>;
- xTokens: XTokensGroup<PolkadexHelper>;
- xcm: XcmGroup<PolkadexHelper>;
- xcmHelper: PolkadexXcmHelperGroup<PolkadexHelper>;
-
- constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
- super(logger, options.helperBase ?? PolkadexHelper);
-
- this.assets = new AssetsGroup(this);
- this.balance = new SubstrateBalanceGroup(this);
- this.xTokens = new XTokensGroup(this);
- this.xcm = new XcmGroup(this, 'polkadotXcm');
- this.xcmHelper = new PolkadexXcmHelperGroup(this);
- }
-
- getSudo<T extends PolkadexHelper>() {
- // eslint-disable-next-line @typescript-eslint/naming-convention
- const SudoHelperType = SudoHelper(this.helperBase);
- return this.clone(SudoHelperType) as T;
- }
-}
-
-// eslint-disable-next-line @typescript-eslint/naming-convention
-function ScheduledUniqueHelper<T extends UniqueHelperConstructor>(Base: T) {
- return class extends Base {
- scheduleFn: 'schedule' | 'scheduleAfter';
- blocksNum: number;
- options: ISchedulerOptions;
-
- constructor(...args: any[]) {
- const logger = args[0] as ILogger;
- const options = args[1] as {
- scheduleFn: 'schedule' | 'scheduleAfter',
- blocksNum: number,
- options: ISchedulerOptions
- };
-
- super(logger);
-
- this.scheduleFn = options.scheduleFn;
- this.blocksNum = options.blocksNum;
- this.options = options.options;
- }
-
- executeExtrinsic(sender: IKeyringPair, scheduledExtrinsic: string, scheduledParams: any[], expectSuccess?: boolean): Promise<ITransactionResult> {
- const scheduledTx = this.constructApiCall(scheduledExtrinsic, scheduledParams);
-
- const mandatorySchedArgs = [
- this.blocksNum,
- this.options.periodic ? [this.options.periodic.period, this.options.periodic.repetitions] : null,
- this.options.priority ?? null,
- scheduledTx,
- ];
-
- let schedArgs;
- let scheduleFn;
-
- if(this.options.scheduledId) {
- schedArgs = [this.options.scheduledId!, ...mandatorySchedArgs];
-
- if(this.scheduleFn == 'schedule') {
- scheduleFn = 'scheduleNamed';
- } else if(this.scheduleFn == 'scheduleAfter') {
- scheduleFn = 'scheduleNamedAfter';
- }
- } else {
- schedArgs = mandatorySchedArgs;
- scheduleFn = this.scheduleFn;
- }
-
- const extrinsic = 'api.tx.scheduler.' + scheduleFn;
-
- return super.executeExtrinsic(
- sender,
- extrinsic as any,
- schedArgs,
- expectSuccess,
- );
- }
- };
-}
-
-// eslint-disable-next-line @typescript-eslint/naming-convention
-function SudoHelper<T extends ChainHelperBaseConstructor>(Base: T) {
- return class extends Base {
- constructor(...args: any[]) {
- super(...args);
- }
-
- async executeExtrinsic(
- sender: IKeyringPair,
- extrinsic: string,
- params: any[],
- expectSuccess?: boolean,
- options: Partial<SignerOptions> | null = null,
- ): Promise<ITransactionResult> {
- const call = this.constructApiCall(extrinsic, params);
- const result = await super.executeExtrinsic(
- sender,
- 'api.tx.sudo.sudo',
- [call],
- expectSuccess,
- options,
- );
-
- if(result.status === 'Fail') return result;
-
- const data = (result.result.events.find(x => x.event.section == 'sudo' && x.event.method == 'Sudid')?.event.data as any).sudoResult;
- if(data.isErr) {
- if(data.asErr.isModule) {
- const error = (result.result.events[1].event.data as any).sudoResult.asErr.asModule;
- const metaError = super.getApi()?.registry.findMetaError(error);
- throw new Error(`${metaError.section}.${metaError.name}`);
- } else if(data.asErr.isToken) {
- throw new Error(`Token: ${data.asErr.asToken}`);
- }
- // May be [object Object] in case of unhandled non-unit enum
- throw new Error(`Misc: ${data.asErr.toHuman()}`);
- }
- return result;
- }
- async executeExtrinsicUncheckedWeight(
- sender: IKeyringPair,
- extrinsic: string,
- params: any[],
- expectSuccess?: boolean,
- options: Partial<SignerOptions> | null = null,
- ): Promise<ITransactionResult> {
- const call = this.constructApiCall(extrinsic, params);
- const result = await super.executeExtrinsic(
- sender,
- 'api.tx.sudo.sudoUncheckedWeight',
- [call, {refTime: 0, proofSize: 0}],
- expectSuccess,
- options,
- );
-
- if(result.status === 'Fail') return result;
-
- const data = (result.result.events.find(x => x.event.section == 'sudo' && x.event.method == 'Sudid')?.event.data as any).sudoResult;
- if(data.isErr) {
- if(data.asErr.isModule) {
- const error = (result.result.events[1].event.data as any).sudoResult.asErr.asModule;
- const metaError = super.getApi()?.registry.findMetaError(error);
- throw new Error(`${metaError.section}.${metaError.name}`);
- } else if(data.asErr.isToken) {
- throw new Error(`Token: ${data.asErr.asToken}`);
- }
- // May be [object Object] in case of unhandled non-unit enum
- throw new Error(`Misc: ${data.asErr.toHuman()}`);
- }
- return result;
- }
- };
-}
-
export class UniqueBaseCollection {
helper: UniqueHelper;
collectionId: number;
@@ -4272,29 +3081,8 @@
async burn(signer: TSigner) {
return await this.helper.collection.burn(signer, this.collectionId);
- }
-
- scheduleAt<T extends UniqueHelper>(
- executionBlockNumber: number,
- options: ISchedulerOptions = {},
- ) {
- const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);
- return new UniqueBaseCollection(this.collectionId, scheduledHelper);
- }
-
- scheduleAfter<T extends UniqueHelper>(
- blocksBeforeExecution: number,
- options: ISchedulerOptions = {},
- ) {
- const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);
- return new UniqueBaseCollection(this.collectionId, scheduledHelper);
- }
-
- getSudo<T extends UniqueHelper>() {
- return new UniqueBaseCollection(this.collectionId, this.helper.getSudo<T>());
}
}
-
export class UniqueNFTCollection extends UniqueBaseCollection {
getTokenObject(tokenId: number) {
@@ -4386,29 +3174,8 @@
async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {
return await this.helper.nft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);
- }
-
- scheduleAt<T extends UniqueHelper>(
- executionBlockNumber: number,
- options: ISchedulerOptions = {},
- ) {
- const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);
- return new UniqueNFTCollection(this.collectionId, scheduledHelper);
- }
-
- scheduleAfter<T extends UniqueHelper>(
- blocksBeforeExecution: number,
- options: ISchedulerOptions = {},
- ) {
- const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);
- return new UniqueNFTCollection(this.collectionId, scheduledHelper);
- }
-
- getSudo<T extends UniqueHelper>() {
- return new UniqueNFTCollection(this.collectionId, this.helper.getSudo<T>());
}
}
-
export class UniqueRFTCollection extends UniqueBaseCollection {
getTokenObject(tokenId: number) {
@@ -4512,29 +3279,8 @@
async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {
return await this.helper.rft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);
- }
-
- scheduleAt<T extends UniqueHelper>(
- executionBlockNumber: number,
- options: ISchedulerOptions = {},
- ) {
- const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);
- return new UniqueRFTCollection(this.collectionId, scheduledHelper);
- }
-
- scheduleAfter<T extends UniqueHelper>(
- blocksBeforeExecution: number,
- options: ISchedulerOptions = {},
- ) {
- const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);
- return new UniqueRFTCollection(this.collectionId, scheduledHelper);
- }
-
- getSudo<T extends UniqueHelper>() {
- return new UniqueRFTCollection(this.collectionId, this.helper.getSudo<T>());
}
}
-
export class UniqueFTCollection extends UniqueBaseCollection {
async getBalance(addressObj: ICrossAccountId) {
@@ -4579,29 +3325,8 @@
async approveTokens(signer: TSigner, toAddressObj: ICrossAccountId, amount = 1n) {
return await this.helper.ft.approveTokens(signer, this.collectionId, toAddressObj, amount);
- }
-
- scheduleAt<T extends UniqueHelper>(
- executionBlockNumber: number,
- options: ISchedulerOptions = {},
- ) {
- const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);
- return new UniqueFTCollection(this.collectionId, scheduledHelper);
- }
-
- scheduleAfter<T extends UniqueHelper>(
- blocksBeforeExecution: number,
- options: ISchedulerOptions = {},
- ) {
- const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);
- return new UniqueFTCollection(this.collectionId, scheduledHelper);
- }
-
- getSudo<T extends UniqueHelper>() {
- return new UniqueFTCollection(this.collectionId, this.helper.getSudo<T>());
}
}
-
export class UniqueBaseToken {
collection: UniqueNFTCollection | UniqueRFTCollection;
@@ -4640,29 +3365,8 @@
nestingAccount() {
return this.collection.helper.util.getTokenAccount(this);
- }
-
- scheduleAt<T extends UniqueHelper>(
- executionBlockNumber: number,
- options: ISchedulerOptions = {},
- ) {
- const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);
- return new UniqueBaseToken(this.tokenId, scheduledCollection);
- }
-
- scheduleAfter<T extends UniqueHelper>(
- blocksBeforeExecution: number,
- options: ISchedulerOptions = {},
- ) {
- const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);
- return new UniqueBaseToken(this.tokenId, scheduledCollection);
- }
-
- getSudo<T extends UniqueHelper>() {
- return new UniqueBaseToken(this.tokenId, this.collection.getSudo<T>());
}
}
-
export class UniqueNFToken extends UniqueBaseToken {
collection: UniqueNFTCollection;
@@ -4718,26 +3422,6 @@
async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId) {
return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj);
- }
-
- scheduleAt<T extends UniqueHelper>(
- executionBlockNumber: number,
- options: ISchedulerOptions = {},
- ) {
- const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);
- return new UniqueNFToken(this.tokenId, scheduledCollection);
- }
-
- scheduleAfter<T extends UniqueHelper>(
- blocksBeforeExecution: number,
- options: ISchedulerOptions = {},
- ) {
- const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);
- return new UniqueNFToken(this.tokenId, scheduledCollection);
- }
-
- getSudo<T extends UniqueHelper>() {
- return new UniqueNFToken(this.tokenId, this.collection.getSudo<T>());
}
}
@@ -4807,25 +3491,5 @@
async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount = 1n) {
return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj, amount);
- }
-
- scheduleAt<T extends UniqueHelper>(
- executionBlockNumber: number,
- options: ISchedulerOptions = {},
- ) {
- const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);
- return new UniqueRFToken(this.tokenId, scheduledCollection);
- }
-
- scheduleAfter<T extends UniqueHelper>(
- blocksBeforeExecution: number,
- options: ISchedulerOptions = {},
- ) {
- const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);
- return new UniqueRFToken(this.tokenId, scheduledCollection);
- }
-
- getSudo<T extends UniqueHelper>() {
- return new UniqueRFToken(this.tokenId, this.collection.getSudo<T>());
}
}
tests/src/util/playgrounds/unique.xcm.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/util/playgrounds/unique.xcm.ts
@@ -0,0 +1,371 @@
+import {ApiPromise, WsProvider} from '@polkadot/api';
+import {IKeyringPair} from '@polkadot/types/types';
+import {ChainHelperBase, EthereumBalanceGroup, HelperGroup, SubstrateBalanceGroup, UniqueHelper} from './unique';
+import {ILogger, TSigner, TSubstrateAccount} from './types';
+import {AcalaAssetMetadata, DemocracyStandardAccountVote, IForeignAssetMetadata, MoonbeamAssetInfo} from './types.xcm';
+
+
+export class XcmChainHelper extends ChainHelperBase {
+ async connect(wsEndpoint: string, _listeners?: any): Promise<void> {
+ const wsProvider = new WsProvider(wsEndpoint);
+ this.api = new ApiPromise({
+ provider: wsProvider,
+ });
+ await this.api.isReadyOrError;
+ this.network = await UniqueHelper.detectNetwork(this.api);
+ }
+}
+
+class AcalaAssetRegistryGroup extends HelperGroup<AcalaHelper> {
+ async registerForeignAsset(signer: TSigner, destination: any, metadata: AcalaAssetMetadata) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.assetRegistry.registerForeignAsset', [destination, metadata], true);
+ }
+}
+
+class MoonbeamAssetManagerGroup extends HelperGroup<MoonbeamHelper> {
+ makeRegisterForeignAssetProposal(assetInfo: MoonbeamAssetInfo) {
+ const apiPrefix = 'api.tx.assetManager.';
+
+ const registerTx = this.helper.constructApiCall(
+ apiPrefix + 'registerForeignAsset',
+ [assetInfo.location, assetInfo.metadata, assetInfo.existentialDeposit, assetInfo.isSufficient],
+ );
+
+ const setUnitsTx = this.helper.constructApiCall(
+ apiPrefix + 'setAssetUnitsPerSecond',
+ [assetInfo.location, assetInfo.unitsPerSecond, assetInfo.numAssetsWeightHint],
+ );
+
+ const batchCall = this.helper.getApi().tx.utility.batchAll([registerTx, setUnitsTx]);
+ const encodedProposal = batchCall?.method.toHex() || '';
+ return encodedProposal;
+ }
+
+ async assetTypeId(location: any) {
+ return await this.helper.callRpc('api.query.assetManager.assetTypeId', [location]);
+ }
+}
+
+class MoonbeamDemocracyGroup extends HelperGroup<MoonbeamHelper> {
+ notePreimagePallet: string;
+
+ constructor(helper: MoonbeamHelper, options: { [key: string]: any } = {}) {
+ super(helper);
+ this.notePreimagePallet = options.notePreimagePallet;
+ }
+
+ async notePreimage(signer: TSigner, encodedProposal: string) {
+ await this.helper.executeExtrinsic(signer, `api.tx.${this.notePreimagePallet}.notePreimage`, [encodedProposal], true);
+ }
+
+ externalProposeMajority(proposal: any) {
+ return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposal]);
+ }
+
+ fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) {
+ return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);
+ }
+
+ async referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);
+ }
+}
+
+class MoonbeamCollectiveGroup extends HelperGroup<MoonbeamHelper> {
+ collective: string;
+
+ constructor(helper: MoonbeamHelper, collective: string) {
+ super(helper);
+
+ this.collective = collective;
+ }
+
+ async propose(signer: TSigner, threshold: number, proposalHash: string, lengthBound: number) {
+ await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [threshold, proposalHash, lengthBound], true);
+ }
+
+ async vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {
+ await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true);
+ }
+
+ async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: any, lengthBound: number) {
+ await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true);
+ }
+
+ async proposalCount() {
+ return Number(await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, []));
+ }
+}
+
+class PolkadexXcmHelperGroup<T extends ChainHelperBase> extends HelperGroup<T> {
+ async whitelistToken(signer: TSigner, assetId: any) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.xcmHelper.whitelistToken', [assetId], true);
+ }
+}
+
+export class ForeignAssetsGroup extends HelperGroup<UniqueHelper> {
+ async register(signer: TSigner, ownerAddress: TSubstrateAccount, location: any, metadata: IForeignAssetMetadata) {
+ await this.helper.executeExtrinsic(
+ signer,
+ 'api.tx.foreignAssets.registerForeignAsset',
+ [ownerAddress, location, metadata],
+ true,
+ );
+ }
+
+ async update(signer: TSigner, foreignAssetId: number, location: any, metadata: IForeignAssetMetadata) {
+ await this.helper.executeExtrinsic(
+ signer,
+ 'api.tx.foreignAssets.updateForeignAsset',
+ [foreignAssetId, location, metadata],
+ true,
+ );
+ }
+}
+
+export class XcmGroup<T extends ChainHelperBase> extends HelperGroup<T> {
+ palletName: string;
+
+ constructor(helper: T, palletName: string) {
+ super(helper);
+
+ this.palletName = palletName;
+ }
+
+ async limitedReserveTransferAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number, weightLimit: any) {
+ await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.limitedReserveTransferAssets`, [destination, beneficiary, assets, feeAssetItem, weightLimit], true);
+ }
+
+ async setSafeXcmVersion(signer: TSigner, version: number) {
+ await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.forceDefaultXcmVersion`, [version], true);
+ }
+
+ async teleportAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number) {
+ await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.teleportAssets`, [destination, beneficiary, assets, feeAssetItem], true);
+ }
+
+ async teleportNativeAsset(signer: TSigner, destinationParaId: number, targetAccount: Uint8Array, amount: bigint, xcmVersion = 3) {
+ const destinationContent = {
+ parents: 0,
+ interior: {
+ X1: {
+ Parachain: destinationParaId,
+ },
+ },
+ };
+
+ const beneficiaryContent = {
+ parents: 0,
+ interior: {
+ X1: {
+ AccountId32: {
+ network: 'Any',
+ id: targetAccount,
+ },
+ },
+ },
+ };
+
+ const assetsContent = [
+ {
+ id: {
+ Concrete: {
+ parents: 0,
+ interior: 'Here',
+ },
+ },
+ fun: {
+ Fungible: amount,
+ },
+ },
+ ];
+
+ let destination;
+ let beneficiary;
+ let assets;
+
+ if(xcmVersion == 2) {
+ destination = {V1: destinationContent};
+ beneficiary = {V1: beneficiaryContent};
+ assets = {V1: assetsContent};
+
+ } else if(xcmVersion == 3) {
+ destination = {V2: destinationContent};
+ beneficiary = {V2: beneficiaryContent};
+ assets = {V2: assetsContent};
+
+ } else {
+ throw Error('Unknown XCM version: ' + xcmVersion);
+ }
+
+ const feeAssetItem = 0;
+
+ await this.teleportAssets(signer, destination, beneficiary, assets, feeAssetItem);
+ }
+
+ async send(signer: IKeyringPair, destination: any, message: any) {
+ await this.helper.executeExtrinsic(
+ signer,
+ `api.tx.${this.palletName}.send`,
+ [
+ destination,
+ message,
+ ],
+ true,
+ );
+ }
+}
+
+export class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {
+ async transfer(signer: TSigner, currencyId: any, amount: bigint, destination: any, destWeight: any) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transfer', [currencyId, amount, destination, destWeight], true);
+ }
+
+ async transferMultiasset(signer: TSigner, asset: any, destination: any, destWeight: any) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMultiasset', [asset, destination, destWeight], true);
+ }
+
+ async transferMulticurrencies(signer: TSigner, currencies: any[], feeItem: number, destLocation: any, destWeight: any) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMulticurrencies', [currencies, feeItem, destLocation, destWeight], true);
+ }
+}
+
+
+
+export class TokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {
+ async accounts(address: string, currencyId: any) {
+ const {free} = (await this.helper.callRpc('api.query.tokens.accounts', [address, currencyId])).toJSON() as any;
+ return BigInt(free);
+ }
+}
+
+export class AssetsGroup<T extends ChainHelperBase> extends HelperGroup<T> {
+ async create(signer: TSigner, assetId: number, admin: string, minimalBalance: bigint) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.assets.create', [assetId, admin, minimalBalance], true);
+ }
+
+ async setMetadata(signer: TSigner, assetId: number, name: string, symbol: string, decimals: number) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.assets.setMetadata', [assetId, name, symbol, decimals], true);
+ }
+
+ async mint(signer: TSigner, assetId: number, beneficiary: string, amount: bigint) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.assets.mint', [assetId, beneficiary, amount], true);
+ }
+
+ async account(assetId: string | number, address: string) {
+ const accountAsset = (
+ await this.helper.callRpc('api.query.assets.account', [assetId, address])
+ ).toJSON()! as any;
+
+ if(accountAsset !== null) {
+ return BigInt(accountAsset['balance']);
+ } else {
+ return null;
+ }
+ }
+}
+
+export class RelayHelper extends XcmChainHelper {
+ balance: SubstrateBalanceGroup<RelayHelper>;
+ xcm: XcmGroup<RelayHelper>;
+
+ constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
+ super(logger, options.helperBase ?? RelayHelper);
+
+ this.balance = new SubstrateBalanceGroup(this);
+ this.xcm = new XcmGroup(this, 'xcmPallet');
+ }
+}
+
+export class WestmintHelper extends XcmChainHelper {
+ balance: SubstrateBalanceGroup<WestmintHelper>;
+ xcm: XcmGroup<WestmintHelper>;
+ assets: AssetsGroup<WestmintHelper>;
+ xTokens: XTokensGroup<WestmintHelper>;
+
+ constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
+ super(logger, options.helperBase ?? WestmintHelper);
+
+ this.balance = new SubstrateBalanceGroup(this);
+ this.xcm = new XcmGroup(this, 'polkadotXcm');
+ this.assets = new AssetsGroup(this);
+ this.xTokens = new XTokensGroup(this);
+ }
+}
+
+export class MoonbeamHelper extends XcmChainHelper {
+ balance: EthereumBalanceGroup<MoonbeamHelper>;
+ assetManager: MoonbeamAssetManagerGroup;
+ assets: AssetsGroup<MoonbeamHelper>;
+ xTokens: XTokensGroup<MoonbeamHelper>;
+ democracy: MoonbeamDemocracyGroup;
+ collective: {
+ council: MoonbeamCollectiveGroup,
+ techCommittee: MoonbeamCollectiveGroup,
+ };
+
+ constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
+ super(logger, options.helperBase ?? MoonbeamHelper);
+
+ this.balance = new EthereumBalanceGroup(this);
+ this.assetManager = new MoonbeamAssetManagerGroup(this);
+ this.assets = new AssetsGroup(this);
+ this.xTokens = new XTokensGroup(this);
+ this.democracy = new MoonbeamDemocracyGroup(this, options);
+ this.collective = {
+ council: new MoonbeamCollectiveGroup(this, 'councilCollective'),
+ techCommittee: new MoonbeamCollectiveGroup(this, 'techCommitteeCollective'),
+ };
+ }
+}
+
+export class AstarHelper extends XcmChainHelper {
+ balance: SubstrateBalanceGroup<AstarHelper>;
+ assets: AssetsGroup<AstarHelper>;
+ xcm: XcmGroup<AstarHelper>;
+
+ constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
+ super(logger, options.helperBase ?? AstarHelper);
+
+ this.balance = new SubstrateBalanceGroup(this);
+ this.assets = new AssetsGroup(this);
+ this.xcm = new XcmGroup(this, 'polkadotXcm');
+ }
+}
+
+export class AcalaHelper extends XcmChainHelper {
+ balance: SubstrateBalanceGroup<AcalaHelper>;
+ assetRegistry: AcalaAssetRegistryGroup;
+ xTokens: XTokensGroup<AcalaHelper>;
+ tokens: TokensGroup<AcalaHelper>;
+ xcm: XcmGroup<AcalaHelper>;
+
+ constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
+ super(logger, options.helperBase ?? AcalaHelper);
+
+ this.balance = new SubstrateBalanceGroup(this);
+ this.assetRegistry = new AcalaAssetRegistryGroup(this);
+ this.xTokens = new XTokensGroup(this);
+ this.tokens = new TokensGroup(this);
+ this.xcm = new XcmGroup(this, 'polkadotXcm');
+ }
+}
+
+export class PolkadexHelper extends XcmChainHelper {
+ assets: AssetsGroup<PolkadexHelper>;
+ balance: SubstrateBalanceGroup<PolkadexHelper>;
+ xTokens: XTokensGroup<PolkadexHelper>;
+ xcm: XcmGroup<PolkadexHelper>;
+ xcmHelper: PolkadexXcmHelperGroup<PolkadexHelper>;
+
+ constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
+ super(logger, options.helperBase ?? PolkadexHelper);
+
+ this.assets = new AssetsGroup(this);
+ this.balance = new SubstrateBalanceGroup(this);
+ this.xTokens = new XTokensGroup(this);
+ this.xcm = new XcmGroup(this, 'polkadotXcm');
+ this.xcmHelper = new PolkadexXcmHelperGroup(this);
+ }
+}
+