difftreelog
feat enrich playgrounds with xcm capability
in: master
4 files changed
tests/src/util/playgrounds/index.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/index.ts
+++ b/tests/src/util/playgrounds/index.ts
@@ -7,16 +7,18 @@
import {Context} from 'mocha';
import config from '../../config';
import '../../interfaces/augment-api-events';
-import {DevUniqueHelper, SilentLogger, SilentConsole} from './unique.dev';
+import {ChainHelperBase} from './unique';
+import {ILogger} from './types';
+import {DevUniqueHelper, SilentLogger, SilentConsole, DevMoonbeamHelper, DevMoonriverHelper, DevAcalaHelper, DevKaruraHelper, DevRelayHelper} from './unique.dev';
chai.use(chaiAsPromised);
export const expect = chai.expect;
-export async function usingPlaygrounds(code: (helper: DevUniqueHelper, privateKey: (seed: string) => IKeyringPair) => Promise<void>, url: string = config.substrateUrl) {
+async function usingPlaygroundsGeneral<T extends ChainHelperBase>(helperType: new(logger: ILogger) => T, url: string, code: (helper: T, privateKey: (seed: string) => IKeyringPair) => Promise<void>) {
const silentConsole = new SilentConsole();
silentConsole.enable();
- const helper = new DevUniqueHelper(new SilentLogger());
+ const helper = new helperType(new SilentLogger());
try {
await helper.connect(url);
@@ -30,8 +32,33 @@
}
}
-usingPlaygrounds.atUrl = (url: string, code: (helper: DevUniqueHelper, privateKey: (seed: string) => IKeyringPair) => Promise<void>) => {
- return usingPlaygrounds(code, url);
+export const usingPlaygrounds = (code: (helper: DevUniqueHelper, privateKey: (seed: string) => IKeyringPair) => Promise<void>, url: string = config.substrateUrl) => {
+ return usingPlaygroundsGeneral<DevUniqueHelper>(DevUniqueHelper, url, code);
+};
+
+// TODO specific type
+export const usingStatemintPlaygrounds = async (url: string, code: (helper: DevUniqueHelper, privateKey: (seed: string) => IKeyringPair) => Promise<void>) => {
+ return usingPlaygroundsGeneral<DevUniqueHelper>(DevUniqueHelper, url, code);
+};
+
+export const usingRelayPlaygrounds = async (url: string, code: (helper: DevRelayHelper, privateKey: (seed: string) => IKeyringPair) => Promise<void>) => {
+ return usingPlaygroundsGeneral<DevRelayHelper>(DevRelayHelper, url, code);
+};
+
+export const usingAcalaPlaygrounds = async (url: string, code: (helper: DevAcalaHelper, privateKey: (seed: string) => IKeyringPair) => Promise<void>) => {
+ return usingPlaygroundsGeneral<DevAcalaHelper>(DevAcalaHelper, url, code);
+};
+
+export const usingKaruraPlaygrounds = async (url: string, code: (helper: DevKaruraHelper, privateKey: (seed: string) => IKeyringPair) => Promise<void>) => {
+ return usingPlaygroundsGeneral<DevKaruraHelper>(DevAcalaHelper, url, code);
+};
+
+export const usingMoonbeamPlaygrounds = async (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => IKeyringPair) => Promise<void>) => {
+ return usingPlaygroundsGeneral<DevMoonbeamHelper>(DevMoonbeamHelper, url, code);
+};
+
+export const usingMoonriverPlaygrounds = async (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => IKeyringPair) => Promise<void>) => {
+ return usingPlaygroundsGeneral<DevMoonriverHelper>(DevMoonriverHelper, url, code);
};
export enum Pallets {
tests/src/util/playgrounds/types.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/types.ts
+++ b/tests/src/util/playgrounds/types.ts
@@ -179,8 +179,41 @@
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 type TSubstrateAccount = string;
export type TEthereumAccount = string;
export type TApiAllowedListeners = 'connected' | 'disconnected' | 'error' | 'ready' | 'decorated';
export type TUniqueNetworks = 'opal' | 'quartz' | 'unique';
+export type TSiblingNetworkds = 'moonbeam' | 'moonriver' | 'acala' | 'karura' | 'westmint';
+export type TRelayNetworks = 'rococo' | 'westend';
+export type TNetworks = TUniqueNetworks | TSiblingNetworkds | TRelayNetworks;
export type TSigner = IKeyringPair; // | 'string'
tests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/unique.dev.ts
+++ b/tests/src/util/playgrounds/unique.dev.ts
@@ -2,10 +2,11 @@
// SPDX-License-Identifier: Apache-2.0
import {mnemonicGenerate} from '@polkadot/util-crypto';
-import {UniqueHelper} from './unique';
-import {ApiPromise, WsProvider} from '@polkadot/api';
+import {UniqueHelper, MoonbeamHelper, ChainHelperBase, AcalaHelper, RelayHelper} 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} from './types';
export class SilentLogger {
@@ -52,7 +53,6 @@
console.warn = this.consoleWarn;
}
}
-
export class DevUniqueHelper extends UniqueHelper {
/**
@@ -108,6 +108,36 @@
}
}
+export class DevRelayHelper extends RelayHelper {}
+
+export class DevMoonbeamHelper extends MoonbeamHelper {
+ account: MoonbeamAccountGroup;
+ wait: WaitGroup;
+
+ constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
+ options.helperBase = options.helperBase ?? DevMoonbeamHelper;
+
+ super(logger, options);
+ this.account = new MoonbeamAccountGroup(this);
+ this.wait = new WaitGroup(this);
+ }
+}
+
+export class DevMoonriverHelper extends DevMoonbeamHelper {}
+
+export class DevAcalaHelper extends AcalaHelper {
+ wait: WaitGroup;
+
+ constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
+ options.helperBase = options.helperBase ?? DevAcalaHelper;
+
+ super(logger, options);
+ this.wait = new WaitGroup(this);
+ }
+}
+
+export class DevKaruraHelper extends DevAcalaHelper {}
+
class ArrangeGroup {
helper: DevUniqueHelper;
@@ -245,10 +275,43 @@
}
}
+class MoonbeamAccountGroup {
+ helper: MoonbeamHelper;
+
+ _alithAccount: IKeyringPair;
+ _baltatharAccount: IKeyringPair;
+ _dorothyAccount: IKeyringPair;
+
+ constructor(helper: MoonbeamHelper) {
+ this.helper = helper;
+
+ const keyring = new Keyring({type: 'ethereum'});
+ const alithPrivateKey = '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133';
+ const baltatharPrivateKey = '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b';
+ const dorothyPrivateKey = '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68';
+
+ this._alithAccount = keyring.addFromUri(alithPrivateKey, undefined, 'ethereum');
+ this._baltatharAccount = keyring.addFromUri(baltatharPrivateKey, undefined, 'ethereum');
+ this._dorothyAccount = keyring.addFromUri(dorothyPrivateKey, undefined, 'ethereum');
+ }
+
+ alithAccount() {
+ return this._alithAccount;
+ }
+
+ baltatharAccount() {
+ return this._baltatharAccount;
+ }
+
+ dorothyAccount() {
+ return this._dorothyAccount;
+ }
+}
+
class WaitGroup {
- helper: DevUniqueHelper;
+ helper: ChainHelperBase;
- constructor(helper: DevUniqueHelper) {
+ constructor(helper: ChainHelperBase) {
this.helper = helper;
}
@@ -296,6 +359,40 @@
});
});
}
+
+ async event(maxBlocksToWait: number, eventSection: string, eventMethod: string) {
+ // eslint-disable-next-line no-async-promise-executor
+ const promise = new Promise<EventRecord | null>(async (resolve) => {
+ const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads(async header => {
+ const blockNumber = header.number.toHuman();
+ const blockHash = header.hash;
+ const eventIdStr = `${eventSection}.${eventMethod}`;
+ const waitLimitStr = `wait blocks remaining: ${maxBlocksToWait}`;
+
+ console.log(`[Block #${blockNumber}] Waiting for event \`${eventIdStr}\` (${waitLimitStr})`);
+
+ const apiAt = await this.helper.getApi().at(blockHash);
+ const eventRecords = await apiAt.query.system.events();
+
+ const neededEvent = eventRecords.find(r => {
+ return r.event.section == eventSection && r.event.method == eventMethod;
+ });
+
+ if (neededEvent) {
+ unsubscribe();
+ resolve(neededEvent);
+ } else if (maxBlocksToWait > 0) {
+ maxBlocksToWait--;
+ } else {
+ console.log(`Event \`${eventIdStr}\` is NOT found`);
+
+ unsubscribe();
+ resolve(null);
+ }
+ });
+ });
+ return promise;
+ }
}
class AdminGroup {
tests/src/util/playgrounds/unique.tsdiffbeforeafterboth9import {ApiInterfaceEvents, SignerOptions} from '@polkadot/api/types';9import {ApiInterfaceEvents, SignerOptions} from '@polkadot/api/types';10import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm} from '@polkadot/util-crypto';10import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm} from '@polkadot/util-crypto';11import {IKeyringPair} from '@polkadot/types/types';11import {IKeyringPair} from '@polkadot/types/types';12import {IApiListeners, IBlock, IEvent, IChainProperties, ICollectionCreationOptions, ICollectionLimits, ICollectionPermissions, ICrossAccountId, ICrossAccountIdLower, ILogger, INestingPermissions, IProperty, IStakingInfo, ISchedulerOptions, ISubstrateBalance, IToken, ITokenPropertyPermission, ITransactionResult, IUniqueHelperLog, TApiAllowedListeners, TEthereumAccount, TSigner, TSubstrateAccount, TUniqueNetworks, IForeignAssetMetadata} from './types';12import {IApiListeners, IBlock, IEvent, IChainProperties, ICollectionCreationOptions, ICollectionLimits, ICollectionPermissions, ICrossAccountId, ICrossAccountIdLower, ILogger, INestingPermissions, IProperty, IStakingInfo, ISchedulerOptions, ISubstrateBalance, IToken, ITokenPropertyPermission, ITransactionResult, IUniqueHelperLog, TApiAllowedListeners, TEthereumAccount, TSigner, TSubstrateAccount, IForeignAssetMetadata, TNetworks, MoonbeamAssetInfo, DemocracyStandardAccountVote, AcalaAssetMetadata} from './types';131314export class CrossAccountId implements ICrossAccountId {14export class CrossAccountId implements ICrossAccountId {15 Substrate?: TSubstrateAccount;15 Substrate?: TSubstrateAccount;308 }308 }309}309}310310311class ChainHelperBase {311export class ChainHelperBase {312 helperBase: any;313312 transactionStatus = UniqueUtil.transactionStatus;314 transactionStatus = UniqueUtil.transactionStatus;313 chainLogType = UniqueUtil.chainLogType;315 chainLogType = UniqueUtil.chainLogType;314 util: typeof UniqueUtil;316 util: typeof UniqueUtil;315 eventHelper: typeof UniqueEventHelper;317 eventHelper: typeof UniqueEventHelper;316 logger: ILogger;318 logger: ILogger;317 api: ApiPromise | null;319 api: ApiPromise | null;318 forcedNetwork: TUniqueNetworks | null;320 forcedNetwork: TNetworks | null;319 network: TUniqueNetworks | null;321 network: TNetworks | null;320 chainLog: IUniqueHelperLog[];322 chainLog: IUniqueHelperLog[];321 children: ChainHelperBase[];323 children: ChainHelperBase[];324 address: AddressGroup;325 chain: ChainGroup;322326323 constructor(logger?: ILogger) {327 constructor(logger?: ILogger, helperBase?: any) {328 this.helperBase = helperBase;329324 this.util = UniqueUtil;330 this.util = UniqueUtil;325 this.eventHelper = UniqueEventHelper;331 this.eventHelper = UniqueEventHelper;330 this.network = null;336 this.network = null;331 this.chainLog = [];337 this.chainLog = [];332 this.children = [];338 this.children = [];339 this.address = new AddressGroup(this);340 this.chain = new ChainGroup(this);333 }341 }334342343 clone(helperCls: ChainHelperBaseConstructor, options: {[key: string]: any} = {}) {344 Object.setPrototypeOf(helperCls.prototype, this);345 const newHelper = new helperCls(this.logger, options);346347 newHelper.api = this.api;348 newHelper.network = this.network;349 newHelper.forceNetwork = this.forceNetwork;350351 this.children.push(newHelper);352353 return newHelper;354 }355335 getApi(): ApiPromise {356 getApi(): ApiPromise {336 if(this.api === null) throw Error('API not initialized');357 if(this.api === null) throw Error('API not initialized');337 return this.api;358 return this.api;341 this.chainLog = [];362 this.chainLog = [];342 }363 }343364344 forceNetwork(value: TUniqueNetworks): void {365 forceNetwork(value: TNetworks): void {345 this.forcedNetwork = value;366 this.forcedNetwork = value;346 }367 }347368367 this.network = null;388 this.network = null;368 }389 }369390370 static async detectNetwork(api: ApiPromise): Promise<TUniqueNetworks> {391 static async detectNetwork(api: ApiPromise): Promise<TNetworks> {371 const spec = (await api.query.system.lastRuntimeUpgrade()).toJSON() as any;392 const spec = (await api.query.system.lastRuntimeUpgrade()).toJSON() as any;393 const xcmChains = ['rococo', 'westend', 'westmint', 'acala', 'karura', 'moonbeam', 'moonriver'];394395 if(xcmChains.indexOf(spec.specName) > -1) return spec.specName;396372 if(['quartz', 'unique'].indexOf(spec.specName) > -1) return spec.specName;397 if(['quartz', 'unique'].indexOf(spec.specName) > -1) return spec.specName;373 return 'opal';398 return 'opal';374 }399 }375400376 static async detectNetworkByWsEndpoint(wsEndpoint: string): Promise<TUniqueNetworks> {401 static async detectNetworkByWsEndpoint(wsEndpoint: string): Promise<TNetworks> {377 const api = new ApiPromise({provider: new WsProvider(wsEndpoint)});402 const api = new ApiPromise({provider: new WsProvider(wsEndpoint)});378 await api.isReady;403 await api.isReady;379404384 return network;409 return network;385 }410 }386411387 static async createConnection(wsEndpoint: string, listeners?: IApiListeners, network?: TUniqueNetworks | null): Promise<{412 static async createConnection(wsEndpoint: string, listeners?: IApiListeners, network?: TNetworks | null): Promise<{388 api: ApiPromise;413 api: ApiPromise;389 network: TUniqueNetworks;414 network: TNetworks;390 }> {415 }> {416 console.log('createConnection network = ', network);391 if(typeof network === 'undefined' || network === null) network = 'opal';417 if(typeof network === 'undefined' || network === null) network = 'opal';392 const supportedRPC = {418 const supportedRPC = {393 opal: {419 opal: {399 unique: {425 unique: {400 unique: require('@unique-nft/unique-mainnet-types/definitions').unique.rpc,426 unique: require('@unique-nft/unique-mainnet-types/definitions').unique.rpc,401 },427 },428 rococo: {},429 westend: {},430 moonbeam: {},431 moonriver: {},432 acala: {},433 karura: {},434 westmint: {},402 };435 };403 if(!supportedRPC.hasOwnProperty(network)) network = await this.detectNetworkByWsEndpoint(wsEndpoint);436 if(!supportedRPC.hasOwnProperty(network)) network = await this.detectNetworkByWsEndpoint(wsEndpoint);404 const rpc = supportedRPC[network];437 const rpc = supportedRPC[network];590}623}591624592625593class HelperGroup {626class HelperGroup<T extends ChainHelperBase> {594 helper: UniqueHelper;627 helper: T;595628596 constructor(uniqueHelper: UniqueHelper) {629 constructor(uniqueHelper: T) {597 this.helper = uniqueHelper;630 this.helper = uniqueHelper;598 }631 }599}632}600633601634602class CollectionGroup extends HelperGroup {635class CollectionGroup extends HelperGroup<UniqueHelper> {603 /**636 /**604 * Get number of blocks when sponsored transaction is available.637 * Get number of blocks when sponsored transaction is available.605 *638 *2000}2033}20012034200220352003class ChainGroup extends HelperGroup {2036class ChainGroup extends HelperGroup<ChainHelperBase> {2004 /**2037 /**2005 * Get system properties of a chain2038 * Get system properties of a chain2006 * @example getChainProperties();2039 * @example getChainProperties();2054 }2087 }2055}2088}205620892090class SubstrateBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2091 /**2092 * Get substrate address balance2093 * @param address substrate address2094 * @example getSubstrate("5GrwvaEF5zXb26Fz...")2095 * @returns amount of tokens on address2096 */2097 async getSubstrate(address: TSubstrateAccount): Promise<bigint> {2098 return (await this.helper.callRpc('api.query.system.account', [address])).data.free.toBigInt();2099 }205721002058class BalanceGroup extends HelperGroup {2101 /**2102 * Transfer tokens to substrate address2103 * @param signer keyring of signer2104 * @param address substrate address of a recipient2105 * @param amount amount of tokens to be transfered2106 * @example transferToSubstrate(aliceKeyring, "5GrwvaEF5zXb26Fz...", 100_000_000_000n);2107 * @returns ```true``` if extrinsic success, otherwise ```false```2108 */2109 async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2110 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true/*, `Unable to transfer balance from ${this.helper.getSignerAddress(signer)} to ${address}`*/);21112112 let transfer = {from: null, to: null, amount: 0n} as any;2113 result.result.events.forEach(({event: {data, method, section}}) => {2114 if ((section === 'balances') && (method === 'Transfer')) {2115 transfer = {2116 from: this.helper.address.normalizeSubstrate(data[0]),2117 to: this.helper.address.normalizeSubstrate(data[1]),2118 amount: BigInt(data[2]),2119 };2120 }2121 });2122 const isSuccess = this.helper.address.normalizeSubstrate(typeof signer === 'string' ? signer : signer.address) === transfer.from 2123 && this.helper.address.normalizeSubstrate(address) === transfer.to 2124 && BigInt(amount) === transfer.amount;2125 return isSuccess;2126 }21272128 /**2129 * Get full substrate balance including free, miscFrozen, feeFrozen, and reserved2130 * @param address substrate address2131 * @returns2132 */2133 async getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2134 const accountInfo = (await this.helper.callRpc('api.query.system.account', [address])).data;2135 return {free: accountInfo.free.toBigInt(), miscFrozen: accountInfo.miscFrozen.toBigInt(), feeFrozen: accountInfo.feeFrozen.toBigInt(), reserved: accountInfo.reserved.toBigInt()};2136 }2137}21382139class EthereumBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2140 /**2141 * Get ethereum address balance2142 * @param address ethereum address2143 * @example getEthereum("0x9F0583DbB855d...")2144 * @returns amount of tokens on address2145 */2146 async getEthereum(address: TEthereumAccount): Promise<bigint> {2147 return (await this.helper.callRpc('api.rpc.eth.getBalance', [address])).toBigInt();2148 }21492150 /**2151 * Transfer tokens to address2152 * @param signer keyring of signer2153 * @param address Ethereum address of a recipient2154 * @param amount amount of tokens to be transfered2155 * @example transferToEthereum(alithKeyring, "0x9F0583DbB855d...", 100_000_000_000n);2156 * @returns ```true``` if extrinsic success, otherwise ```false```2157 */2158 async transferToEthereum(signer: TSigner, address: TEthereumAccount, amount: bigint | string): Promise<boolean> {2159 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true);21602161 let transfer = {from: null, to: null, amount: 0n} as any;2162 result.result.events.forEach(({event: {data, method, section}}) => {2163 if ((section === 'balances') && (method === 'Transfer')) {2164 transfer = {2165 from: data[0].toString(),2166 to: data[1].toString(),2167 amount: BigInt(data[2]),2168 };2169 }2170 });2171 const isSuccess = (typeof signer === 'string' ? signer : signer.address) === transfer.from 2172 && address === transfer.to 2173 && BigInt(amount) === transfer.amount;2174 return isSuccess;2175 }2176}21772178class BalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2179 subBalanceGroup: SubstrateBalanceGroup<T>;2180 ethBalanceGroup: EthereumBalanceGroup<T>;21812182 constructor(helper: T) {2183 super(helper);2184 this.subBalanceGroup = new SubstrateBalanceGroup(helper);2185 this.ethBalanceGroup = new EthereumBalanceGroup(helper);2186 }21872059 getCollectionCreationPrice(): bigint {2188 getCollectionCreationPrice(): bigint {2060 return 2n * this.helper.balance.getOneTokenNominal();2189 return 2n * this.getOneTokenNominal();2061 }2190 }2062 /**2191 /**2063 * Representation of the native token in the smallest unit - one OPAL (OPL), QUARTZ (QTZ), or UNIQUE (UNQ).2192 * Representation of the native token in the smallest unit - one OPAL (OPL), QUARTZ (QTZ), or UNIQUE (UNQ).2076 * @returns amount of tokens on address2205 * @returns amount of tokens on address2077 */2206 */2078 async getSubstrate(address: TSubstrateAccount): Promise<bigint> {2207 async getSubstrate(address: TSubstrateAccount): Promise<bigint> {2079 return (await this.helper.callRpc('api.query.system.account', [address])).data.free.toBigInt();2208 return this.subBalanceGroup.getSubstrate(address);2080 }2209 }208122102082 /**2211 /**2085 * @returns2214 * @returns2086 */2215 */2087 async getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2216 async getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2088 const accountInfo = (await this.helper.callRpc('api.query.system.account', [address])).data;2217 return this.subBalanceGroup.getSubstrateFull(address);2089 return {free: accountInfo.free.toBigInt(), miscFrozen: accountInfo.miscFrozen.toBigInt(), feeFrozen: accountInfo.feeFrozen.toBigInt(), reserved: accountInfo.reserved.toBigInt()};2090 }2218 }209122192092 /**2220 /**2096 * @returns amount of tokens on address2224 * @returns amount of tokens on address2097 */2225 */2098 async getEthereum(address: TEthereumAccount): Promise<bigint> {2226 async getEthereum(address: TEthereumAccount): Promise<bigint> {2099 return (await this.helper.callRpc('api.rpc.eth.getBalance', [address])).toBigInt();2227 return this.ethBalanceGroup.getEthereum(address);2100 }2228 }210122292102 /**2230 /**2108 * @returns ```true``` if extrinsic success, otherwise ```false```2236 * @returns ```true``` if extrinsic success, otherwise ```false```2109 */2237 */2110 async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2238 async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2111 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true/*, `Unable to transfer balance from ${this.helper.getSignerAddress(signer)} to ${address}`*/);2239 return this.subBalanceGroup.transferToSubstrate(signer, address, amount);21122113 let transfer = {from: null, to: null, amount: 0n} as any;2114 result.result.events.forEach(({event: {data, method, section}}) => {2115 if ((section === 'balances') && (method === 'Transfer')) {2116 transfer = {2117 from: this.helper.address.normalizeSubstrate(data[0]),2118 to: this.helper.address.normalizeSubstrate(data[1]),2119 amount: BigInt(data[2]),2120 };2121 }2122 });2123 const isSuccess = this.helper.address.normalizeSubstrate(typeof signer === 'string' ? signer : signer.address) === transfer.from 2124 && this.helper.address.normalizeSubstrate(address) === transfer.to 2125 && BigInt(amount) === transfer.amount;2126 return isSuccess;2127 }2240 }2128}2241}2129224221302131class AddressGroup extends HelperGroup {2243class AddressGroup extends HelperGroup<ChainHelperBase> {2132 /**2244 /**2133 * Normalizes the address to the specified ss58 format, by default ```42```.2245 * Normalizes the address to the specified ss58 format, by default ```42```.2134 * @param address substrate address2246 * @param address substrate address2172 }2284 }2173}2285}217422862175class StakingGroup extends HelperGroup {2287class StakingGroup extends HelperGroup<UniqueHelper> {2176 /**2288 /**2177 * Stake tokens for App Promotion2289 * Stake tokens for App Promotion2178 * @param signer keyring of signer2290 * @param signer keyring of signer2258 }2370 }2259}2371}226023722261class SchedulerGroup extends HelperGroup {2373class SchedulerGroup extends HelperGroup<UniqueHelper> {2262 constructor(helper: UniqueHelper) {2374 constructor(helper: UniqueHelper) {2263 super(helper);2375 super(helper);2264 }2376 }2314 }2426 }2315}2427}231624282317class ForeignAssetsGroup extends HelperGroup {2429class ForeignAssetsGroup extends HelperGroup<UniqueHelper> {2318 constructor(helper: UniqueHelper) {2319 super(helper);2320 }23212322 async register(signer: TSigner, ownerAddress: TSubstrateAccount, location: any, metadata: IForeignAssetMetadata) {2430 async register(signer: TSigner, ownerAddress: TSubstrateAccount, location: any, metadata: IForeignAssetMetadata) {2323 await this.helper.executeExtrinsic(2431 await this.helper.executeExtrinsic(2338 }2446 }2339}2447}234024482449class XcmGroup<T extends ChainHelperBase> extends HelperGroup<T> {2450 palletName: string;24512452 constructor(helper: T, palletName: string) {2453 super(helper);24542455 this.palletName = palletName;2456 }24572458 async limitedReserveTransferAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number, weightLimit: number) {2459 await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.limitedReserveTransferAssets`, [destination, beneficiary, assets, feeAssetItem, {Limited: weightLimit}], true);2460 }2461}24622463class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {2464 async transfer(signer: TSigner, currencyId: any, amount: bigint, destination: any, destWeight: number) {2465 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transfer', [currencyId, amount, destination, destWeight], true);2466 }24672468 async transferMultiasset(signer: TSigner, asset: any, destination: any, destWeight: number) {2469 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMultiasset', [asset, destination, destWeight], true);2470 }2471}24722473class TokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {2474 async accounts(address: string, currencyId: any) {2475 const {free} = (await this.helper.callRpc('api.query.tokens.accounts', [address, currencyId])).toJSON() as any;2476 return BigInt(free);2477 }2478}24792480class AcalaAssetRegistryGroup extends HelperGroup<AcalaHelper> {2481 async registerForeignAsset(signer: TSigner, destination: any, metadata: AcalaAssetMetadata) {2482 await this.helper.executeExtrinsic(signer, 'api.tx.assetRegistry.registerForeignAsset', [destination, metadata], true);2483 }2484}24852486class MoonbeamAssetManagerGroup extends HelperGroup<MoonbeamHelper> {2487 makeRegisterForeignAssetProposal(assetInfo: MoonbeamAssetInfo) {2488 const apiPrefix = 'api.tx.assetManager.';24892490 const registerTx = this.helper.constructApiCall(2491 apiPrefix + 'registerForeignAsset',2492 [assetInfo.location, assetInfo.metadata, assetInfo.existentialDeposit, assetInfo.isSufficient],2493 );24942495 const setUnitsTx = this.helper.constructApiCall(2496 apiPrefix + 'setAssetUnitsPerSecond',2497 [assetInfo.location, assetInfo.unitsPerSecond, assetInfo.numAssetsWeightHint],2498 );24992500 const batchCall = this.helper.getApi().tx.utility.batchAll([registerTx, setUnitsTx]);2501 const encodedProposal = batchCall?.method.toHex() || '';2502 return encodedProposal;2503 }25042505 async assetTypeId(location: any) {2506 return await this.helper.callRpc('api.query.assetManager.assetTypeId', [location]);2507 }2508}25092510class MoonbeamAssetsGroup extends HelperGroup<MoonbeamHelper> {2511 async account(assetId: string, address: string) {2512 const accountAsset = (2513 await this.helper.callRpc('api.query.assets.account', [assetId, address])2514 ).toJSON()! as any;25152516 if (accountAsset !== null) {2517 return BigInt(accountAsset['balance']);2518 } else {2519 return null;2520 }2521 }2522}25232524class MoonbeamDemocracyGroup extends HelperGroup<MoonbeamHelper> {2525 async notePreimage(signer: TSigner, encodedProposal: string) {2526 await this.helper.executeExtrinsic(signer, 'api.tx.democracy.notePreimage', [encodedProposal], true);2527 }25282529 externalProposeMajority(proposalHash: string) {2530 return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposalHash]);2531 }25322533 fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) {2534 return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);2535 }25362537 async referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {2538 await this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);2539 }2540}25412542class MoonbeamCollectiveGroup extends HelperGroup<MoonbeamHelper> {2543 collective: string;25442545 constructor(helper: MoonbeamHelper, collective: string) {2546 super(helper);25472548 this.collective = collective;2549 }25502551 async propose(signer: TSigner, threshold: number, proposalHash: string, lengthBound: number) {2552 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [threshold, proposalHash, lengthBound], true);2553 }25542555 async vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {2556 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true);2557 }25582559 async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: number, lengthBound: number) {2560 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true);2561 }25622563 async proposalCount() {2564 return Number(await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, []));2565 }2566}25672568export type ChainHelperBaseConstructor = new(...args: any[]) => ChainHelperBase;2341export type UniqueHelperConstructor = new(...args: any[]) => UniqueHelper;2569export type UniqueHelperConstructor = new(...args: any[]) => UniqueHelper;234225702343export class UniqueHelper extends ChainHelperBase {2571export class UniqueHelper extends ChainHelperBase {2344 helperBase: any;2572 balance: BalanceGroup<UniqueHelper>;23452346 chain: ChainGroup;2347 balance: BalanceGroup;2348 address: AddressGroup;2349 collection: CollectionGroup;2573 collection: CollectionGroup;2350 nft: NFTGroup;2574 nft: NFTGroup;2351 rft: RFTGroup;2575 rft: RFTGroup;2352 ft: FTGroup;2576 ft: FTGroup;2353 staking: StakingGroup;2577 staking: StakingGroup;2354 scheduler: SchedulerGroup;2578 scheduler: SchedulerGroup;2355 foreignAssets: ForeignAssetsGroup;2579 foreignAssets: ForeignAssetsGroup;2580 xcm: XcmGroup<UniqueHelper>;2581 xTokens: XTokensGroup<UniqueHelper>;2582 tokens: TokensGroup<UniqueHelper>;235625832357 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2584 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2358 super(logger);2585 super(logger, options.helperBase ?? UniqueHelper);235925862360 this.helperBase = options.helperBase ?? UniqueHelper;23612362 this.chain = new ChainGroup(this);2363 this.balance = new BalanceGroup(this);2587 this.balance = new BalanceGroup(this);2364 this.address = new AddressGroup(this);2588 this.address = new AddressGroup(this);2365 this.collection = new CollectionGroup(this);2589 this.collection = new CollectionGroup(this);2369 this.staking = new StakingGroup(this);2593 this.staking = new StakingGroup(this);2370 this.scheduler = new SchedulerGroup(this);2594 this.scheduler = new SchedulerGroup(this);2371 this.foreignAssets = new ForeignAssetsGroup(this);2595 this.foreignAssets = new ForeignAssetsGroup(this);2596 this.xcm = new XcmGroup(this, 'polkadotXcm');2597 this.xTokens = new XTokensGroup(this);2598 this.tokens = new TokensGroup(this);2372 }2599 }237326002374 clone(helperCls: UniqueHelperConstructor, options: {[key: string]: any} = {}) {2601 getSudo<T extends UniqueHelper>() {2602 // eslint-disable-next-line @typescript-eslint/naming-convention2375 Object.setPrototypeOf(helperCls.prototype, this);2603 const SudoHelperType = SudoHelper(this.helperBase);2376 const newHelper = new helperCls(this.logger, options);2604 return this.clone(SudoHelperType) as T;2605 }2606}237726072378 newHelper.api = this.api;2608export class XcmChainHelper extends ChainHelperBase {2609 async connect(wsEndpoint: string, _listeners?: any): Promise<void> {2610 const wsProvider = new WsProvider(wsEndpoint);2611 this.api = new ApiPromise({2612 provider: wsProvider,2613 });2379 newHelper.network = this.network;2614 await this.api.isReadyOrError;2380 newHelper.forceNetwork = this.forceNetwork;2615 this.network = await UniqueHelper.detectNetwork(this.api);2616 }2617}238126182382 this.children.push(newHelper);2619export class RelayHelper extends XcmChainHelper {2620 xcm: XcmGroup<RelayHelper>;238326212384 return newHelper;2622 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2623 super(logger, options.helperBase ?? RelayHelper);26242625 this.xcm = new XcmGroup(this, 'xcmPallet');2385 }2626 }2627}238626282387 getSudo<T extends UniqueHelper>() {2629export class MoonbeamHelper extends XcmChainHelper {2630 balance: EthereumBalanceGroup<MoonbeamHelper>;2631 assetManager: MoonbeamAssetManagerGroup;2632 assets: MoonbeamAssetsGroup;2633 xTokens: XTokensGroup<MoonbeamHelper>;2634 democracy: MoonbeamDemocracyGroup;2635 collective: {2636 council: MoonbeamCollectiveGroup,2637 techCommittee: MoonbeamCollectiveGroup,2638 };26392640 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2641 super(logger, options.helperBase ?? MoonbeamHelper);26422643 this.balance = new EthereumBalanceGroup(this);2644 this.assetManager = new MoonbeamAssetManagerGroup(this);2645 this.assets = new MoonbeamAssetsGroup(this);2646 this.xTokens = new XTokensGroup(this);2647 this.democracy = new MoonbeamDemocracyGroup(this);2648 this.collective = {2649 council: new MoonbeamCollectiveGroup(this, 'councilCollective'),2650 techCommittee: new MoonbeamCollectiveGroup(this, 'techCommitteeCollective'),2651 };2652 }2653}26542655export class AcalaHelper extends XcmChainHelper {2656 balance: SubstrateBalanceGroup<AcalaHelper>;2657 assetRegistry: AcalaAssetRegistryGroup;2658 xTokens: XTokensGroup<AcalaHelper>;2659 tokens: TokensGroup<AcalaHelper>;26602661 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2662 super(logger, options.helperBase ?? AcalaHelper);26632664 this.balance = new SubstrateBalanceGroup(this);2665 this.assetRegistry = new AcalaAssetRegistryGroup(this);2666 this.xTokens = new XTokensGroup(this);2667 this.tokens = new TokensGroup(this);2668 }26692670 getSudo<T extends AcalaHelper>() {2388 // eslint-disable-next-line @typescript-eslint/naming-convention2671 // eslint-disable-next-line @typescript-eslint/naming-convention2389 const SudoHelperType = SudoUniqueHelper(this.helperBase);2672 const SudoHelperType = SudoHelper(this.helperBase);2390 return this.clone(SudoHelperType) as T;2673 return this.clone(SudoHelperType) as T;2391 }2674 }2392}2675}2437}2720}243827212439// eslint-disable-next-line @typescript-eslint/naming-convention2722// eslint-disable-next-line @typescript-eslint/naming-convention2440function SudoUniqueHelper<T extends UniqueHelperConstructor>(Base: T) {2723function SudoHelper<T extends ChainHelperBaseConstructor>(Base: T) {2441 return class extends Base {2724 return class extends Base {2442 constructor(...args: any[]) {2725 constructor(...args: any[]) {2443 super(...args);2726 super(...args);